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

SourceCountData Fields AvailablePhone Numbers?
Calendly “New Event” emails~40 unique contactsName, Email, Event Date, Meeting Notes/Questions, Additional GuestsNO
Google Calendar events (Jan-Jul 2025)~5 unique non-Calendly contactsName, Email, Event Date, DescriptionNO
Cal.com booking notificationsZERO in this accountN/AN/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)

#NameEmailNotesStatus
1Brian Akselradbakselrad@gmail.comConfirmed
2Nicole Klaunicole@hypertextllc.comMultiple meetingsConfirmed
3Dominic Kalmsdominic@bgenerous.comwww.BGenerous.comConfirmed
4Christian LeFerchristianlefer@gmail.comConfirmed
5Sofia Sunagasofia@intergen.family+ guest david@intergen.familyConfirmed
6Eileenemwalz@gmail.comFirst name onlyConfirmed
7Dimitri Syrrisdimitri@baotree.ioMultiple meetingsConfirmed
8Adriana Ruizadriana@agamistudios.comMultiple meetingsConfirmed
9Brad Smithbrad@philanthropy.network⚠️ Also used brad.smith@philanthropy.networkConfirmed
10Remzi Bajramiremzib@gmail.comConfirmed
11Maria Dahriehm.dahrieh@earth05.orgMultiple meetingsConfirmed
12Sonnie Deansonnie.dean@menziesmission.orgMultiple meetingsConfirmed
13Tami Kesselmantk@nextgensuccess.comConfirmed
14Alyssa Oplandaopland@redpointps.comConfirmed
15Killu Sanbornksanborn@oxfordfinance.comConfirmed
16Mario Hernandezmario@impactcircle.networkConfirmed
17David (and Sofia)david@intergen.familyPart of Intergen.family pairConfirmed
18Bill Reedreed@regenesisgroup.comConfirmed
19Corrie Dretlercmdretler@gmail.comConfirmed
20Katie Hilbornkatie@globalop.orgRescheduledConfirmed
21Gail Workgwork@oneearthventures.comConfirmed
22Joe Thurmanjoe@talentbloom.aiConfirmed
23Nisha Ranjani Iyernriyer25@gmail.comConfirmed
24Arshanarshan@worksenseai.comFirst name onlyConfirmed
25Jennifer Farrisjennifer.farris@veranahealth.comConfirmed
26Rio Hodgesrio@antler.coConfirmed
27Ken Beitelken.beitel@graphdash.comCanceled
28Adrienne Majcinaadrienne.majcina@questco.netConfirmed
29Samsam@wehateexercise.comFirst name onlyConfirmed
30James Taylorjtaylor@analyticaldatasystems.comConfirmed
31Mark Berrymberry@inari.comConfirmed
32Ray Prushnokray@moonbeam.healthConfirmed
33Lillian Khaytovichlilliankhaytovich@gmail.comConfirmed
34Von Nelsontravelinghog@hotmail.comConfirmed
35Ryan Mullryan@mullingmagpie.comConfirmed
36Amina Abubakaramina@safe-africa.orgFunding freeze concernCanceled
37Danicaassistant@nonprofit.com+ guest christian@instantnonprofit.comCanceled
38MelRose Wild(need to extract from reminder emails)30 Min Connection MeetingConfirmed
39Larry C Johnson(via Calendly Getting Acquainted)Podcast host, The Eight PrinciplesConfirmed

From Google Calendar (Non-Calendly Contacts)

#NameEmailSourceNotes
40Larry C. Johnsonlarry@theeightprinciples.comGoogle Calendar invitePodcast recording
41Payam Safapayam@lightdao.oneReclaim scheduling linkLight DAO discussion
42Paul Foleycalendar@paulnfoley.comGoogle Calendar inviteMultiple coffee/meetings
43Benjamin Caillatengagewithlifecoaching@gmail.comTidyCal bookingCoaching session
44Yovelyovel@letsdopodcast.comGoogle Calendar inviteLet’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?

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)

  1. Verify Baserow credentials are still active (.env check)
  2. List tables → get People table ID
  3. Pull ALL existing People rows → build email dedup map
  4. Check Email field type (must be email not text)
  5. Count existing rows as baseline
  6. Check if approaching row quota limits

Phase 2: Build Intermediate Contact JSON (15 min)

  1. Parse all Calendly “New Event” emails → extract structured data
  2. Parse Google Calendar events → extract non-Calendly contacts
  3. Normalize all emails to lowercase + trimmed
  4. Apply Connected From labels
  5. Output: extracted-contacts-2026-03-11.json

Phase 3: Deduplication (5 min)

  1. Load existing People from Baserow
  2. Email-based dedup (case-insensitive match)
  3. Flag Brad Smith dual-email case
  4. Output: contacts-to-import.json + dedup-report.json
  5. Present to Dmitri for quick review before insertion

Phase 4: Baserow Insertion (5 min)

  1. Batch create new contacts (200-row max per batch)
  2. 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)
  3. Log all inserted row IDs

Phase 5: Verification (5 min)

  1. Compare row count before/after
  2. Spot-check 5 random contacts in Baserow
  3. Verify no duplicate emails exist
  4. Generate summary report

Phase 6: SOP Documentation (15 min)

  1. Step-by-step process document
  2. Field mappings reference
  3. Edge case handling guide
  4. Instructions for running with different email accounts
  5. Save to solanasis-docs/misc/

Risk Mitigation

RiskSeverityMitigation
Duplicate contacts in BaserowHighCode-level dedup using email map before insertion
Baserow unique constraint not availableMediumEnforce in extraction script; document as manual check
Calendar connector timeoutMediumFall back to Gmail invitation emails for supplementary data
Partial/incorrect namesLowInsert as-is with flag; clean up manually later
Phone numbers missingMediumDocument gap; plan enrichment via Calendly CSV export
Script fails mid-batchLowIdempotent insertion (skip existing emails); retry safe

Pro Tips

  1. Reply-To header is the most reliable email source — Calendly sets it to the invitee’s actual email, cleaner than parsing HTML body
  2. Calendly “Additional Guests” field can contain extra contacts worth capturing (like david@intergen.family alongside sofia@intergen.family)
  3. Baserow batch API supports up to 200 rows per request — much faster than individual inserts
  4. For the other email account: Cal.com booking notifications may have different HTML structure than Calendly — will need separate parser
  5. 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 in baserow/ 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 contacts
  • solanasis-scripts/baserow/dedup-analysis-2026-03-12.json — Full dedup report
  • solanasis-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:

Remaining Next Steps

  1. Repeat process for second email account (connect different Gmail in Cowork)
  2. Enrich phone numbers via Calendly/Cal.com CSV exports
  3. Look up full names for first-name-only contacts