This workflow corresponds to n8n.io template #14415 — we link there as the canonical source.
This workflow follows the Agent → Google Calendar recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"nodes": [
{
"id": "fff1926b-1669-47f2-baea-c32bbe3a5c06",
"name": "Daily 4PM Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
448,
640
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 16
}
]
}
},
"typeVersion": 1.3
},
{
"id": "52022f83-ef69-4315-8199-ba3e39a8d9c6",
"name": "Calculate Tomorrow Window",
"type": "n8n-nodes-base.code",
"position": [
672,
640
],
"parameters": {
"jsCode": "// Calculate 24-26 hour window from now\nconst now = DateTime.now();\nconst timeMin = now.plus({ hours: 24 }).toISO();\nconst timeMax = now.plus({ hours: 26 }).toISO();\n\nreturn [{\n json: {\n timeMin,\n timeMax,\n checkTime: now.toISO()\n }\n}];"
},
"typeVersion": 2
},
{
"id": "a9ff6e12-a240-4847-8ff0-88fdcc59e249",
"name": "Fetch Calendar Events",
"type": "n8n-nodes-base.googleCalendar",
"position": [
896,
640
],
"parameters": {
"options": {
"orderBy": "startTime"
},
"calendar": {
"mode": "list",
"value": "primary"
},
"operation": "getAll",
"returnAll": true
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "a5bbe08d-fe4c-4ce2-91d4-adb985378a31",
"name": "Extract Call Details",
"type": "n8n-nodes-base.code",
"position": [
1120,
640
],
"parameters": {
"jsCode": "const items = $input.all();\nconst keywords = ['discovery', 'call', 'meeting', 'nca'];\n\n// \u2500\u2500 Deduplication via static data \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Track processed eventIds so the same calendar event is never\n// briefed twice, even if it falls inside the time-window on\n// consecutive daily runs.\nconst staticData = $getWorkflowStaticData('global');\nif (!staticData.processedEvents) {\n staticData.processedEvents = {};\n}\n\n// Purge entries older than 30 days to prevent memory bloat\nconst THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;\nconst now = Date.now();\nfor (const [id, ts] of Object.entries(staticData.processedEvents)) {\n if (now - Number(ts) > THIRTY_DAYS_MS) {\n delete staticData.processedEvents[id];\n }\n}\n\nconst callEvents = items.filter(item => {\n const title = (item.json.summary || '').toLowerCase();\n return keywords.some(kw => title.includes(kw));\n});\n\nif (callEvents.length === 0) {\n return [];\n}\n\n// Filter out already-processed events\nconst newEvents = callEvents.filter(event => {\n const eventId = event.json.id;\n if (staticData.processedEvents[eventId]) {\n return false; // already briefed \u2014 skip\n }\n return true;\n});\n\nif (newEvents.length === 0) {\n return []; // all events already processed\n}\n\n// Extract org name and contact from attendees\nconst results = newEvents.map(event => {\n const attendees = event.json.attendees || [];\n const externalAttendees = attendees.filter(a => !a.organizer && !a.self);\n const contact = externalAttendees[0] || {};\n const contactName = contact.displayName || contact.email || 'Unknown Contact';\n const contactEmail = contact.email || '';\n\n let orgName = 'Unknown Organization';\n if (contactEmail && contactEmail.includes('@')) {\n const domain = contactEmail.split('@')[1];\n if (domain && !domain.includes('gmail') && !domain.includes('yahoo') && !domain.includes('hotmail') && !domain.includes('outlook')) {\n orgName = domain.split('.')[0].charAt(0).toUpperCase() + domain.split('.')[0].slice(1);\n }\n }\n\n const startTime = event.json.start?.dateTime || event.json.start?.date || '';\n\n // Mark as processed BEFORE returning so next run skips it\n staticData.processedEvents[event.json.id] = Date.now();\n\n return {\n json: {\n eventId: event.json.id,\n title: event.json.summary,\n contactName,\n contactEmail,\n orgName,\n startTime,\n calendarLink: event.json.htmlLink || ''\n }\n };\n});\n\nreturn results;"
},
"typeVersion": 2
},
{
"id": "306eaca9-536d-4d79-8ec6-34ed82e8f4f6",
"name": "Research Brief Generator",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1408,
640
],
"parameters": {
"text": "=You are a research analyst for [your company], a capacity building consultancy that helps organizations strengthen their internal capabilities \u2014 including leadership development, team performance, operational systems, and strategic planning.\n\nPrepare a pre-call research brief for an upcoming call: \"{{ $json.title }}\"\nOrganization: {{ $json.orgName }}\nContact person: {{ $json.contactName }} ({{ $json.contactEmail }})\nScheduled: {{ $json.startTime.substring(0, 10) }} at {{ $json.startTime.substring(11, 16) }}\n\n## Your Research Process\n\nYou have access to a web scraping tool. USE IT to gather real information. Do not guess or fabricate.\n\n**Step 1: Research the organization**\n- Scrape the organization's website (try https://{{ $json.contactEmail.split('@')[1] }} if the contact has a corporate email)\n- Look for: what they do, their mission, team size, recent news, programs, or services\n\n**Step 2: Research the contact person**\n- Scrape their LinkedIn profile or any public bio page if you can find one\n- Look for: their role, background, how long they've been at the org\n\n**Step 3: Look for recent news**\n- Try scraping a Google search results page or news page about the organization\n- Look for: funding, leadership changes, expansions, partnerships, or public announcements\n\n## Output Requirements\n\nBased on your research, provide:\n\n1. **Organization Background**: What does this organization do? What sector are they in? Approximate size if known. Any recent news, funding rounds, or leadership changes. Cite specific facts you found from scraping \u2014 do not make up information.\n\n2. **Capacity Signals**: Based on what you found on their website and public information, what capacity gaps might they be experiencing? Be specific to THIS organization \u2014 e.g. if they're scaling fast, mention leadership pipeline gaps; if they're a nonprofit, mention donor dependency or team burnout risks.\n\n3. **Suggested Opening Questions**: Provide 3 conversation starters that reference specific things you found in your research. Questions should uncover their real needs without being presumptuous. Frame them around capacity building themes.\n\n4. **What to Avoid**: Any sensitive topics, recent controversies, or areas to handle carefully based on what you found. If nothing specific, note general best practices for this type of organization.\n\nKeep the total brief under 300 words. Be specific and factual \u2014 cite what you found. If a scrape fails or returns no useful data, note that and provide your best analysis based on available information.",
"options": {},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 3.1
},
{
"id": "41e16f2f-348d-4989-ae13-e3031d3dcc0e",
"name": "GPT-5 Mini Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
1312,
752
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "cx/gpt-5.5",
"cachedResultName": "cx/gpt-5.5"
},
"options": {},
"builtInTools": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "541dd42e-11ac-459c-a717-08b129030672",
"name": "Brief Schema Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
1648,
752
],
"parameters": {
"jsonSchemaExample": "{\n \"org_background\": \"TechCorp (techcorp.io) is a mid-size HR-tech SaaS company with approximately 200 employees, founded in 2019. They provide workforce analytics tools for enterprise HR teams. Recently raised Series B ($15M) in Q3 2025 led by Accel. New VP of People (Sarah Chen) appointed January 2026 \u2014 previously at Stripe. Their website highlights rapid expansion into APAC markets.\",\n \"capacity_signals\": \"Post-Series B hypergrowth is likely straining their middle-management layer \u2014 their careers page lists 30+ open roles across 4 new teams. Leadership pipeline gaps are common at this stage. Their blog mentions 'culture growing pains' in a recent post, suggesting team alignment and operational processes haven't scaled with headcount. The APAC expansion adds cross-cultural leadership complexity.\",\n \"suggested_opening_questions\": [\n \"I saw TechCorp recently expanded into APAC \u2014 how has that affected your leadership team's bandwidth and the way you develop managers across regions?\",\n \"Your careers page shows significant hiring across several new teams. How are you approaching the challenge of getting new team leads up to speed while maintaining your culture?\",\n \"With Sarah Chen joining as VP of People earlier this year, has the organization started rethinking how it develops internal leaders at scale?\"\n ],\n \"what_to_avoid\": \"Their Glassdoor reviews (3.4 stars) mention concerns about rapid culture change and manager readiness \u2014 do not reference Glassdoor directly, but listen for signals around team morale and leadership support. Avoid asking about their Series A struggles (reported layoffs in 2023) unless they bring it up.\"\n}"
},
"typeVersion": 1.3
},
{
"id": "2c2293f4-d9db-4ec6-b92a-7f4887dbd9d5",
"name": "Scrape Web Pages",
"type": "@mendable/n8n-nodes-firecrawl.firecrawlTool",
"position": [
1504,
800
],
"parameters": {
"query": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Query', ``, 'string') }}",
"resource": "MapSearch",
"operation": "search",
"requestOptions": {}
},
"credentials": {
"firecrawlApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "ad038152-012c-4678-bc63-51f398aa2185",
"name": "Search Client Folder",
"type": "n8n-nodes-base.googleDrive",
"position": [
1808,
640
],
"parameters": {
"limit": 1,
"filter": {
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive",
"cachedResultUrl": "https://drive.google.com/drive/my-drive",
"cachedResultName": "My Drive"
},
"folderId": {
"__rl": true,
"mode": "list",
"value": "1leAqV8umgHqqBU7IfoG8QpuAd6T2t3Ml"
}
},
"options": {},
"resource": "fileFolder",
"queryString": "={{ $('Extract Call Details').item.json.orgName }}"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3,
"alwaysOutputData": true
},
{
"id": "950ede64-4cdc-4e62-aed7-8c21a6b46517",
"name": "Folder Exists?",
"type": "n8n-nodes-base.if",
"position": [
2032,
640
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "folder-check",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.id }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.3,
"alwaysOutputData": false
},
{
"id": "369c7790-cc18-4f15-aa25-0e0e5fbef0d2",
"name": "Create Client Folder",
"type": "n8n-nodes-base.googleDrive",
"position": [
2256,
736
],
"parameters": {
"name": "={{ new Date($('Extract Call Details').item.json.startTime).toISOString().slice(0, 10) }} - {{ $('Extract Call Details').item.json.orgName }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive",
"cachedResultUrl": "https://drive.google.com/drive/my-drive",
"cachedResultName": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "list",
"value": "root",
"cachedResultUrl": "https://drive.google.com/drive",
"cachedResultName": "/ (Root folder)"
},
"resource": "folder"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "7cc8125f-e2ea-46c7-b177-64107bd2d1da",
"name": "Save Brief to Drive",
"type": "n8n-nodes-base.googleDrive",
"position": [
2464,
624
],
"parameters": {
"name": "=Pre-Call Brief \u2014 {{ $('Extract Call Details').first().json.orgName }}",
"content": "=PRE-CALL RESEARCH BRIEF\n{{ $('Extract Call Details').first().json.orgName }} | {{ $('Extract Call Details').first().json.startTime.substring(0, 10) }}\nContact: {{ $('Extract Call Details').first().json.contactName }}\n============================================================\n\nORGANIZATION BACKGROUND\n{{ $('Research Brief Generator').first().json.output.org_background }}\n\nCAPACITY SIGNALS\n{{ $('Research Brief Generator').first().json.output.capacity_signals }}\n\nSUGGESTED OPENING QUESTIONS\n{{ $('Research Brief Generator').first().json.output.suggested_opening_questions.map((q, i) => (i+1) + '. ' + q).join('\\n') }}\n\nWHAT TO AVOID\n{{ $('Research Brief Generator').first().json.output.what_to_avoid }}\n",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.id || $('Create Client Folder').item.json.id }}"
},
"operation": "createFromText"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "afbe31d0-8e50-43b4-89c1-ca7a53a03b2a",
"name": "Notify Ops Channel",
"type": "n8n-nodes-base.slack",
"position": [
2704,
624
],
"parameters": {
"text": "=\ud83d\udccb *PRE-CALL BRIEF READY*\n\n*Call:* {{ $(\"Extract Call Details\").item.json.title }}\n*Organization:* {{ $(\"Extract Call Details\").item.json.orgName }}\n*Contact:* {{ $(\"Extract Call Details\").item.json.contactName }}\n*When:* {{ $(\"Extract Call Details\").item.json.startTime.substring(0, 10) }} at {{ $(\"Extract Call Details\").item.json.startTime.substring(11, 16) }}\n\n---\n*Background:*\n{{ $('Research Brief Generator').item.json.output.org_background }}\n\n*Capacity Signals:*\n{{ $('Research Brief Generator').item.json.output.capacity_signals }}\n\n*Opening Questions:*\n{{ $('Research Brief Generator').item.json.output.suggested_opening_questions.map((q, i) => (i+1) + '. ' + q).join('\\n') }}\n\n*Watch Out:*\n{{ $('Research Brief Generator').item.json.output.what_to_avoid }}\n---\n\n\ud83d\udcc1 *Client Folder:* https://drive.google.com/drive/u/1/folders/{{ $('Create Client Folder').item.json.id }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "#ops"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.4
},
{
"id": "df8551c1-ebcd-439f-8b48-07bc94766354",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
448,
-48
],
"parameters": {
"width": 448,
"height": 656,
"content": "## Pre-Call Research Brief\n\n### How it works\n1. Run daily at 4pm and calculate a 24\u201326 hour window for \"tomorrow\", then fetch Google Calendar events in that window.\n2. Identify likely client calls by keyword (discovery, call, meeting, NCA) and exclude events already briefed.\n3. Use a web-scraping tool plus the language model to research the organization and contact and generate a concise pre-call brief (background, capacity signals, opening questions, what to avoid).\n4. Save the brief to the client's Google Drive folder (create the folder if missing) and post a notification with the brief summary and Drive link to the ops Slack channel.\n\n### Setup\n- [ ] Connect your Google Calendar account.\n- [ ] Connect Google Drive and select or create the \"CLIENTS\" folder.\n- [ ] Connect Slack and pick the channel to notify (e.g., #ops).\n- [ ] Add your OpenAI API key for the language model.\n- [ ] Add credentials for the web-scraping tool (Firecrawl / Mendable).\n- [ ] Confirm the schedule runs daily at 16:00 (4:00pm) or adjust as needed."
},
"typeVersion": 1
}
],
"connections": {
"Folder Exists?": {
"main": [
[
{
"node": "Save Brief to Drive",
"type": "main",
"index": 0
}
],
[
{
"node": "Create Client Folder",
"type": "main",
"index": 0
}
]
]
},
"GPT-5 Mini Model": {
"ai_languageModel": [
[
{
"node": "Research Brief Generator",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Scrape Web Pages": {
"ai_tool": [
[
{
"node": "Research Brief Generator",
"type": "ai_tool",
"index": 0
}
]
]
},
"Daily 4PM Trigger": {
"main": [
[
{
"node": "Calculate Tomorrow Window",
"type": "main",
"index": 0
}
]
]
},
"Brief Schema Parser": {
"ai_outputParser": [
[
{
"node": "Research Brief Generator",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Save Brief to Drive": {
"main": [
[
{
"node": "Notify Ops Channel",
"type": "main",
"index": 0
}
]
]
},
"Create Client Folder": {
"main": [
[
{
"node": "Save Brief to Drive",
"type": "main",
"index": 0
}
]
]
},
"Extract Call Details": {
"main": [
[
{
"node": "Research Brief Generator",
"type": "main",
"index": 0
}
]
]
},
"Search Client Folder": {
"main": [
[
{
"node": "Folder Exists?",
"type": "main",
"index": 0
}
]
]
},
"Fetch Calendar Events": {
"main": [
[
{
"node": "Extract Call Details",
"type": "main",
"index": 0
}
]
]
},
"Research Brief Generator": {
"main": [
[
{
"node": "Search Client Folder",
"type": "main",
"index": 0
}
]
]
},
"Calculate Tomorrow Window": {
"main": [
[
{
"node": "Fetch Calendar Events",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
firecrawlApigoogleCalendarOAuth2ApigoogleDriveOAuth2ApiopenAiApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Imagine walking into every discovery call fully prepared with company background, recent news, and perfectly tailored questions, without spending a single minute prepping.
Source: https://n8n.io/workflows/14415/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow is designed for Japanese-speaking professionals, and learners who want to efficiently stay up to date with practical productivity, lifehack, and efficiency-related insights from Japanese
Splitout Googlecalendar. Uses splitOut, slack, googleCalendar, outputParserStructured. Scheduled trigger; 33 nodes.
This n8n workflow takes Slack conversations and turns them into Calendar events complete with accurate date and times and location information. Adding and removing attendees are also managed automatic
🧾 An intelligent automation system that turns Google Meet recordings into structured meeting notes — integrating Fireflies.ai, OpenAI GPT-4.1-mini, Notion, Slack, Google Drive, and Gmail via n8n.
Ensure suppliers never miss a follow-up by automating overdue purchase order tracking and scheduling. 📦⏰ This workflow checks Airtable every weekday morning for open POs older than seven days without