Skip to content

Google Meet Integration

Google Meet is supported as a platform by the meeting-bot providers (Vexa / Recall.ai), but first-class capture also uses the user’s Google Calendar connection to discover upcoming Meet calls automatically.

sequenceDiagram
participant GCal as Google Calendar
participant Cron as /api/cron/google-sync
participant DB as Postgres
participant Web as Meetings UI
participant Bot as Vexa / Recall
Cron->>GCal: calendar.events.list
Cron->>DB: upsert calendar_events (meeting_url, attendees, is_meet)
Web->>DB: googleMeet.upcoming (owner-scoped)
Web->>DB: attendee emails → leads/contacts/organizations
Web->>Bot: googleMeet.dispatch → dispatchMeetingBot
Bot-->>Web: bot id + status (meetings.calendar_event_id set)

pullGoogleCalendarEvents (lib/integrations/google/calendar-sync.ts) now stores, per imported event:

Column Source
meeting_url hangoutLink, else the conferenceData video entry point, else a Meet link scanned from location/description.
attendees (jsonb) Normalized { email, displayName, responseStatus, organizer, self }[], lowercased and deduped; the organizer is always included.
is_meet (bool) Derived: meeting_url is a valid Google Meet join URL.

Foreign (non-CRM-pushed) events are insert-once. A targeted backfill updates meeting_url/attendees/is_meet on rows imported before this feature existed, without touching other fields.

matchMeetAttendeesToRecords (lib/meetings/google-meet.ts):

  1. Drop attendees whose email belongs to an internal profile (never suggest a teammate).
  2. Exact lower(email) match against contacts (email_id) and leads (email) — highest confidence.
  3. Organization match by attendee email domain (consumer mail domains excluded) against organizations.website.

googleMeet.upcoming filters candidates through filterReadableWorkspaceRecords, so a meetings.read grant never reveals a record the caller cannot read.

googleMeet.dispatch (requires meetings.create):

  1. Validates meetings-linked record edit access when a record is supplied.
  2. Loads the owner-scoped calendar_events row and parses the Meet URL (parseMeetingUrl).
  3. Reuses an in-flight capture (meetings.calendar_event_id with an active status) instead of dispatching twice.
  4. Calls dispatchMeetingBot (the shared seam also used by meetings.startBot), which starts the provider bot and inserts the meetings row with calendar_event_id set.
  5. Records a meeting activity on the linked record.

meeting_bot_config.autoCaptureMeet (Settings → Meeting bot; System Manager only) defaults to false. When enabled, /api/cron/google-sync calls autoDispatchUpcomingMeets after the calendar pull, dispatching the notetaker for events starting within the next 15 minutes. Failures are logged per user and never fail the cron.

Procedure Guard Purpose
googleMeet.status meetings.read Google connection, provider readiness, auto-capture state.
googleMeet.upcoming meetings.read Upcoming Meet calls + readable record candidates.
googleMeet.refresh meetings.read Pull the user’s calendar on demand.
googleMeet.dispatch meetings.create Send the notetaker for one invitation.
googleMeet.setAutoCapture System Manager Toggle the workspace auto-capture opt-in.

0128 adds calendar_events.meeting_url/attendees/is_meet, meetings.calendar_event_id, and supporting indexes.