Email/Calendar → Baserow CRM Contact Extraction Plan
Status: EXECUTED — Extraction complete, import script ready to run Created: 2026-03-11 Last Updated: 2026-03-12 Reviewer Feedback: Incorporated (scored 72/100 → revised to address gaps) Applies to: zasyatkin@gmail.com (primary), then repeatable for other email accounts Decisions: All 7 decision points approved with recommended options (2026-03-11) Results: 44 contacts extracted → 23 already in Baserow, 20 new, 1 no email
Executive Summary
Extract all meeting contacts from Gmail (Calendly notifications) and Google Calendar events, then push them into the Baserow CRM People table. This creates a clean contact database from all past meetings, with deduplication against existing Baserow data.
Data Discovery Findings
What’s Available in This Gmail Account
| Source | Count | Data Fields Available | Phone Numbers? |
|---|---|---|---|
| Calendly “New Event” emails | ~40 unique contacts | Name, Email, Event Date, Meeting Notes/Questions, Additional Guests | NO |
| Google Calendar events (Jan-Jul 2025) | ~5 unique non-Calendly contacts | Name, Email, Event Date, Description | NO |
| Cal.com booking notifications | ZERO in this account | N/A | N/A |
Key Finding: No Phone Numbers
- Calendly notification emails to the host do NOT include phone numbers even when collected via forms
- Phone numbers are stored in Calendly’s system but not included in email notifications
- To get phone numbers, you’d need direct Calendly API access or CSV export from Calendly dashboard
- Recommendation: Leave Phone Number field empty for now. Enrich later via Calendly CSV export or LinkedIn lookup
- This is important for the other email account too — Cal.com booking notifications may or may not include phone numbers depending on configuration
Calendar Connector Limitation
- Google Calendar connector times out when querying ranges >6 months with many events
- Workaround: Use Gmail invitation emails as supplementary source for contacts beyond the Calendar API range
Extracted Contacts (40+ unique from this account)
From Calendly “New Event” Emails (Primary Source)
| # | Name | Notes | Status | |
|---|---|---|---|---|
| 1 | Brian Akselrad | bakselrad@gmail.com | — | Confirmed |
| 2 | Nicole Klau | nicole@hypertextllc.com | Multiple meetings | Confirmed |
| 3 | Dominic Kalms | dominic@bgenerous.com | www.BGenerous.com | Confirmed |
| 4 | Christian LeFer | christianlefer@gmail.com | — | Confirmed |
| 5 | Sofia Sunaga | sofia@intergen.family | + guest david@intergen.family | Confirmed |
| 6 | Eileen | emwalz@gmail.com | First name only | Confirmed |
| 7 | Dimitri Syrris | dimitri@baotree.io | Multiple meetings | Confirmed |
| 8 | Adriana Ruiz | adriana@agamistudios.com | Multiple meetings | Confirmed |
| 9 | Brad Smith | brad@philanthropy.network | ⚠️ Also used brad.smith@philanthropy.network | Confirmed |
| 10 | Remzi Bajrami | remzib@gmail.com | — | Confirmed |
| 11 | Maria Dahrieh | m.dahrieh@earth05.org | Multiple meetings | Confirmed |
| 12 | Sonnie Dean | sonnie.dean@menziesmission.org | Multiple meetings | Confirmed |
| 13 | Tami Kesselman | tk@nextgensuccess.com | — | Confirmed |
| 14 | Alyssa Opland | aopland@redpointps.com | — | Confirmed |
| 15 | Killu Sanborn | ksanborn@oxfordfinance.com | — | Confirmed |
| 16 | Mario Hernandez | mario@impactcircle.network | — | Confirmed |
| 17 | David (and Sofia) | david@intergen.family | Part of Intergen.family pair | Confirmed |
| 18 | Bill Reed | reed@regenesisgroup.com | — | Confirmed |
| 19 | Corrie Dretler | cmdretler@gmail.com | — | Confirmed |
| 20 | Katie Hilborn | katie@globalop.org | Rescheduled | Confirmed |
| 21 | Gail Work | gwork@oneearthventures.com | — | Confirmed |
| 22 | Joe Thurman | joe@talentbloom.ai | — | Confirmed |
| 23 | Nisha Ranjani Iyer | nriyer25@gmail.com | — | Confirmed |
| 24 | Arshan | arshan@worksenseai.com | First name only | Confirmed |
| 25 | Jennifer Farris | jennifer.farris@veranahealth.com | — | Confirmed |
| 26 | Rio Hodges | rio@antler.co | — | Confirmed |
| 27 | Ken Beitel | ken.beitel@graphdash.com | — | Canceled |
| 28 | Adrienne Majcina | adrienne.majcina@questco.net | — | Confirmed |
| 29 | Sam | sam@wehateexercise.com | First name only | Confirmed |
| 30 | James Taylor | jtaylor@analyticaldatasystems.com | — | Confirmed |
| 31 | Mark Berry | mberry@inari.com | — | Confirmed |
| 32 | Ray Prushnok | ray@moonbeam.health | — | Confirmed |
| 33 | Lillian Khaytovich | lilliankhaytovich@gmail.com | — | Confirmed |
| 34 | Von Nelson | travelinghog@hotmail.com | — | Confirmed |
| 35 | Ryan Mull | ryan@mullingmagpie.com | — | Confirmed |
| 36 | Amina Abubakar | amina@safe-africa.org | Funding freeze concern | Canceled |
| 37 | Danica | assistant@nonprofit.com | + guest christian@instantnonprofit.com | Canceled |
| 38 | MelRose Wild | (need to extract from reminder emails) | 30 Min Connection Meeting | Confirmed |
| 39 | Larry C Johnson | (via Calendly Getting Acquainted) | Podcast host, The Eight Principles | Confirmed |
From Google Calendar (Non-Calendly Contacts)
| # | Name | Source | Notes | |
|---|---|---|---|---|
| 40 | Larry C. Johnson | larry@theeightprinciples.com | Google Calendar invite | Podcast recording |
| 41 | Payam Safa | payam@lightdao.one | Reclaim scheduling link | Light DAO discussion |
| 42 | Paul Foley | calendar@paulnfoley.com | Google Calendar invite | Multiple coffee/meetings |
| 43 | Benjamin Caillat | engagewithlifecoaching@gmail.com | TidyCal booking | Coaching session |
| 44 | Yovel | yovel@letsdopodcast.com | Google Calendar invite | Let’s Do Podcast |
Decisions Needed from Dmitri
1. Include canceled meetings? (Amina, Danica, Ken Beitel)
- Recommended: YES — they expressed interest and are valid contacts
- Will mark Connected From as “Calendly (Canceled)”
- Options:
- A) Include all, mark as canceled ← Recommended
- B) Exclude canceled meetings entirely
- C) Include only if the meeting was rescheduled (not fully canceled)
2. Exclude personal/non-business contacts?
- Recommended: Include all, tag appropriately
- Contacts like Benjamin Caillat (life coaching) are on the boundary
- Options:
- A) Include all contacts, add “Personal” tag where applicable ← Recommended
- B) Exclude clearly personal contacts (coaching, doula, family)
- C) Only include contacts that are potential business/partnership contacts
3. Brad Smith has 2 different emails — how to handle?
- brad@philanthropy.network (from one booking)
- brad.smith@philanthropy.network (from another booking)
- Recommended: Use primary email, note the secondary
- Options:
- A) Create one row with brad@philanthropy.network, add note about alternate email ← Recommended
- B) Create two separate rows (one per email)
- C) Skip and flag for manual entry
4. Partial names (Sam, Arshan, Eileen, Danica) — insert as-is?
- Recommended: YES, insert as-is with a tag
- Options:
- A) Insert as-is, add “Needs Full Name” in Notes ← Recommended
- B) Skip contacts without full names
- C) Try to infer last name from email domain (risky)
5. Should we also create Meeting Notes entries?
- Baserow has a Meeting Notes table linked to People
- Recommended: NO for v1 — focus on People first
- Options:
- A) People table only for now ← Recommended (simpler, faster)
- B) Also create Meeting Notes for each unique meeting
- C) Create Meeting Notes only for contacts with substantive notes
6. Connected From field values — what standard?
- Recommended enum values:
- “Calendly” (standard booking)
- “Calendly (Canceled)” (canceled booking)
- “Cal.com” (for other email account)
- “Google Calendar” (direct invite)
- “Reclaim” (Reclaim scheduling link)
- “TidyCal” (TidyCal booking)
7. How to handle the other email account?
- Options:
- A) Same Baserow People table, same dedup logic, just different Gmail source ← Recommended
- B) Separate extraction into different table, merge later
- C) Process both accounts simultaneously
Execution Plan (Post-Approval)
Phase 1: Baserow Preflight (5 min)
- Verify Baserow credentials are still active (
.envcheck) - List tables → get People table ID
- Pull ALL existing People rows → build email dedup map
- Check Email field type (must be
emailnottext) - Count existing rows as baseline
- Check if approaching row quota limits
Phase 2: Build Intermediate Contact JSON (15 min)
- Parse all Calendly “New Event” emails → extract structured data
- Parse Google Calendar events → extract non-Calendly contacts
- Normalize all emails to lowercase + trimmed
- Apply Connected From labels
- Output:
extracted-contacts-2026-03-11.json
Phase 3: Deduplication (5 min)
- Load existing People from Baserow
- Email-based dedup (case-insensitive match)
- Flag Brad Smith dual-email case
- Output:
contacts-to-import.json+dedup-report.json - Present to Dmitri for quick review before insertion
Phase 4: Baserow Insertion (5 min)
- Batch create new contacts (200-row max per batch)
- Field mapping:
- Name → Name
- Email → Email (lowercase)
- Phone Number → (empty — not available from email notifications)
- Notes → Meeting context from Calendly questions field
- Connected From → Connected From (using enum above)
- Log all inserted row IDs
Phase 5: Verification (5 min)
- Compare row count before/after
- Spot-check 5 random contacts in Baserow
- Verify no duplicate emails exist
- Generate summary report
Phase 6: SOP Documentation (15 min)
- Step-by-step process document
- Field mappings reference
- Edge case handling guide
- Instructions for running with different email accounts
- Save to solanasis-docs/misc/
Risk Mitigation
| Risk | Severity | Mitigation |
|---|---|---|
| Duplicate contacts in Baserow | High | Code-level dedup using email map before insertion |
| Baserow unique constraint not available | Medium | Enforce in extraction script; document as manual check |
| Calendar connector timeout | Medium | Fall back to Gmail invitation emails for supplementary data |
| Partial/incorrect names | Low | Insert as-is with flag; clean up manually later |
| Phone numbers missing | Medium | Document gap; plan enrichment via Calendly CSV export |
| Script fails mid-batch | Low | Idempotent insertion (skip existing emails); retry safe |
Pro Tips
- Reply-To header is the most reliable email source — Calendly sets it to the invitee’s actual email, cleaner than parsing HTML body
- Calendly “Additional Guests” field can contain extra contacts worth capturing (like david@intergen.family alongside sofia@intergen.family)
- Baserow batch API supports up to 200 rows per request — much faster than individual inserts
- For the other email account: Cal.com booking notifications may have different HTML structure than Calendly — will need separate parser
- Email normalization gotcha: Some people use plus-addressing (john+calendly@gmail.com vs john@gmail.com) — normalize by stripping the +suffix before the @ for Gmail addresses specifically
Execution Results (2026-03-12)
Extraction Summary
- 44 total contacts extracted from zasyatkin@gmail.com
- Source breakdown: 39 from Calendly, 3 from Google Calendar, 1 from Reclaim, 1 from TidyCal
- 1 contact skipped — MelRose Wild (only in reminder emails, no email extractable)
Dedup Analysis Against Existing Baserow Data (160 People rows from Coda migration)
- 23 contacts already exist in Baserow — will be skipped by email-based dedup
- 20 contacts are truly new — ready for insertion
- Dedup corrections applied: Name-based fuzzy matching caught 5 false positives that were manually corrected:
- David (intergen.family) ≠ David Chang — different person → NEW
- Arshan (worksenseai.com) ≠ Steve Marshank — different person → NEW
- Christian (instantnonprofit.com) ≠ Christian LeFer — different person → NEW
- Katie Hilborn = Katie Hillborn (row 23) — same person, spelling variant → EXISTING
- Adrienne Majcina = Adrienne Milligan Majcina, SHRM-CP (row 133) — same person → EXISTING
Key Environment Discovery
- Credentials location:
solanasis-scripts/.env(NOT inbaserow/subdirectory) - People table ID: 267 (from
solanasis-scripts/scripts/migration-data/table_id_map.json) - Database ID: 54
- VM network limitation: Cowork VM cannot directly call Baserow API (proxy blocks non-allowlisted domains). Import script must be run from user’s local machine.
Files Created
solanasis-scripts/baserow/extracted-contacts-2026-03-11.json— 44 contactssolanasis-scripts/baserow/dedup-analysis-2026-03-12.json— Full dedup reportsolanasis-scripts/baserow/import-contacts.ts— Import script (updated to load .env from parent dir)solanasis-docs/misc/solanasis-email-to-crm-sop.md— Reusable SOP
Import Results (2026-03-13)
Import executed from local machine using npx tsx import-contacts.ts.
Final counts:
- 19 contacts inserted into Baserow People table (batch API, single request)
- 24 contacts skipped as duplicates (email-based dedup against live Baserow data)
- 1 contact skipped (MelRose Wild, no email)
- Final People row count: 179 (was 160)
Pre-import enrichment (critical step): The name-based dedup analysis (23 existing) differed from email-based live dedup (12 existing) because 11 rows from the Coda migration had no Email field set. Without enrichment, the import would have created 11 duplicate rows.
Before running the import:
- 11 existing rows patched with Email — rows 153, 83, 152, 144, 147, 141, 23, 145, 137, 143, 154
- Brad Smith (row 149) — email updated from brad.smith@philanthropy.network to brad@philanthropy.network (Calendly booking email); alias noted in Notes field
- 24 existing rows patched with Connected From — all had empty Connected From from Coda migration
- Arshan (row 134) — already existed in Baserow with email (listed as new in name-based analysis, caught by email dedup)
ESM fix: import-contacts.ts required adding fileURLToPath(import.meta.url) shim for __dirname since the project uses "type": "module".
Files created:
solanasis-scripts/baserow/import-report-2026-03-13.json— Full import report with inserted/skipped contacts
Contacts needing manual follow-up:
- David (david@intergen.family) — first name only
- Eileen (emwalz@gmail.com) — first name only
- Arshan (arshan@worksenseai.com) — first name only (already in Baserow as “Arshan Ahmad”)
- Sam (sam@wehateexercise.com) — first name only
- Danica (assistant@nonprofit.com) — first name only, canceled meeting
- Christian (christian@instantnonprofit.com) — first name only
- Yovel (yovel@letsdopodcast.com) — first name only
- Ken Beitel (ken.beitel@graphdash.com) — meeting was canceled
- Amina Abubakar (amina@safe-africa.org) — meeting was canceled
Remaining Next Steps
- Repeat process for second email account (connect different Gmail in Cowork)
- Enrich phone numbers via Calendly/Cal.com CSV exports
- Look up full names for first-name-only contacts