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.
Data flow
Section titled “Data flow”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)Calendar fields
Section titled “Calendar fields”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.
Record matching
Section titled “Record matching”matchMeetAttendeesToRecords (lib/meetings/google-meet.ts):
- Drop attendees whose email belongs to an internal profile (never suggest a teammate).
- Exact
lower(email)match against contacts (email_id) and leads (email) — highest confidence. - 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.
Dispatch
Section titled “Dispatch”googleMeet.dispatch (requires meetings.create):
- Validates
meetings-linked record edit access when a record is supplied. - Loads the owner-scoped
calendar_eventsrow and parses the Meet URL (parseMeetingUrl). - Reuses an in-flight capture (
meetings.calendar_event_idwith an active status) instead of dispatching twice. - Calls
dispatchMeetingBot(the shared seam also used bymeetings.startBot), which starts the provider bot and inserts themeetingsrow withcalendar_event_idset. - Records a
meetingactivity on the linked record.
Auto-capture (opt-in)
Section titled “Auto-capture (opt-in)”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.
Endpoints
Section titled “Endpoints”| 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. |
Migration
Section titled “Migration”0128 adds calendar_events.meeting_url/attendees/is_meet, meetings.calendar_event_id, and supporting indexes.
