How to sync Granola notes into Lightfield

Last updated: April 28, 2026

How to sync Granola notes into Lightfield

Lightfield doesn't pull in Granola notes automatically — they need to be connected via a workflow. Once set up, the workflow runs hourly, matches your Granola meetings to CRM meetings, and loads the notes in as transcripts. From there, the AI can reference them across your opportunities and accounts.

Step 1: Create a time-based workflow

  • Go to Workflows and create a new workflow

  • Set the trigger to Time-based

  • Switch to Advanced and enter 0 * * * * as the cron expression (runs every hour)

Step 2: Add an Agent step

  • Add an Agent step to the workflow

Step 3: Paste in the sync prompt

In the Agent step prompt, paste the following:


Granola → Lightfield CRM Sync

Context

  • Granola tool output contains timestamps in UTC timezone. Internal CRM tool outputs contain timestamps in the timezone specified in the system prompt.

  • Extract the timezone from the system prompt line: All timestamps are listed in the user's timezone, {timezone}.

  • Match meetings using attendee email overlap + title similarity + time alignment after timezone conversion.

  • Granola MCP tool outputs are saved to workspace files — read them with bashExecutor (cat /workspace/.mcp_results/<filename>.json).

Step 1: Fetch data (parallel calls)

Make these calls simultaneously and wait for all results.

1a. Granola meetings

tool: mcp_granola_list_meetings

text

{  "time_range": "custom",  "custom_start": "<ISO date: 1 days ago>",  "custom_end": "<ISO date: today>"}

1b. CRM meetings

tool: getMeetings

text

{  "description": "meetings from the past 1 days",  "filterExpression": "startDate:>=<1 days ago ISO> && startDate:<=<now ISO>",  "offset": 0,  "sortExpression": ["startDate:desc"]}

Step 2: Fetch Granola meeting details (one at a time)

For each meeting ID from Step 1a, fetch details individually so each result is saved to its own workspace file:

tool: mcp_granola_get_meetings

text

{  "meeting_ids": ["<single_meeting_id>"]}

Each call produces a separate /workspace/.mcp_results/<filename>.json file. Track the mapping of granola_id → file_path for use in Step 4.

Before syncing, extract just the <summary> content into a clean text file (e.g., /workspace/.mcp_results/notes_<granola_id>.txt) using bashExecutor. Only pass the clean file to upsertMeetingTranscript.

Step 3: Match meetings

Use pythonExecutor to match programmatically. Do not match by hand.

  1. Extract the workspace timezone from the system prompt.

  2. Convert Granola UTC timestamps to the workspace timezone using zoneinfo (never hardcode offsets — handle DST correctly):

python

from datetime import datetimefrom zoneinfo import ZoneInfoorg_tz = ZoneInfo(org_timezone)  # from system promptgranola_utc = datetime.fromisoformat(granola_timestamp).replace(tzinfo=ZoneInfo("UTC"))granola_local = granola_utc.astimezone(org_tz)
  1. For each Granola meeting, find the CRM meeting where:

    • Start times are within 30 minutes after timezone conversion, and

    • Attendee emails overlap, and

    • Title is similar (fuzzy match — check if titles share key words, ignoring case and filler words)

  2. Only accept a match when at least two of these three signals are strong (e.g., exact title + time match, or 2+ email overlaps + time match). A single shared attendee with a completely different title is not a valid match.

  3. Output matched rows with: granola_id, granola_file_path, crm_meeting_id, crm_meeting_title, account_name, hasTranscript.

hasTranscript and attendee emails come from the getMeetings output (Step 1b). Skip unmatched meetings on either side.

Step 4: Sync transcripts

For each matched meeting:

  • If hasTranscript is true: do nothing. Log: "Transcript preserved for [title]."

  • If hasTranscript is false: upsert the Granola notes as the CRM transcript using the clean file from Step 2.

tool: upsertMeetingTranscript

text

{  "meetingId": "<crm_meeting_id>",  "meetingTitle": "<meeting title>",  "workspaceFilePath": "<clean notes file from Step 2>",  "fileUrl": null,  "transcriptText": null}

Do not reason about transcript contents. The hasTranscript boolean is the only signal — if it's true, the meeting already has a transcript and you must do nothing.

Step 5: Summarize

Return:

  1. Matched: each Granola → CRM match with meeting title and account name

  2. Synced: which meetings had Granola notes loaded as transcripts

  3. Preserved: which meetings already had transcripts (skipped)

  4. Unmatched: any Granola or CRM meetings that couldn't be matched


Where to find the notes after setup

Once the workflow runs, Granola notes will appear as meeting transcripts on the matching CRM meetings. From there, the AI can surface them when you ask questions about an account or opportunity — you won't need to search for them separately.