This workflow corresponds to n8n.io template #15985 — we link there as the canonical source.
This workflow follows the Gmail → Gmail Trigger 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 →
{
"meta": {
"templateCredsSetupCompleted": false
},
"name": "Meeting Notes to Google Doc powered by easybits",
"tags": [],
"nodes": [
{
"id": "03119c5c-d51a-47f2-a9ed-ac15d6c5d2b6",
"name": "Gmail: Watch Inbox",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
160,
672
],
"parameters": {
"simple": false,
"filters": {
"readStatus": "unread"
},
"options": {
"downloadAttachments": true
},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "82b90a62-1be4-4eeb-84b7-762e5525df56",
"name": "easybits: Extract Meeting Notes",
"type": "@easybits/n8n-nodes-extractor.easybitsExtractor",
"position": [
464,
672
],
"parameters": {},
"typeVersion": 2
},
{
"id": "1ae53bb7-9495-4618-b8f9-4d3088d6fc48",
"name": "Set: Build Doc Body",
"type": "n8n-nodes-base.set",
"position": [
768,
672
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "={\n \"meeting_title\": {{ JSON.stringify($json.data.meeting_title || 'Meeting Notes \u2014 ' + ($json.data.meeting_date || $now.format('yyyy-MM-dd'))) }},\n \"meeting_date\": {{ JSON.stringify($json.data.meeting_date || $now.format('yyyy-MM-dd')) }},\n \"captured_date\": \"{{ $now.format('yyyy-MM-dd') }}\",\n \"sender_email\": \"{{ $('Gmail: Watch Inbox').item.json.from.value[0].address }}\",\n \"attendees_section\": {{ JSON.stringify(\n ($json.data.attendees && $json.data.attendees.length > 0)\n ? 'Attendees:\\n' + $json.data.attendees.map(a => '\u2022 ' + a).join('\\n')\n : 'Attendees: (none listed)'\n ) }},\n \"summary_section\": {{ JSON.stringify(\n $json.data.summary && $json.data.summary.trim().length > 0\n ? $json.data.summary\n : '(No additional summary captured.)'\n ) }},\n \"action_items_section\": {{ JSON.stringify(\n ($json.data.action_items && $json.data.action_items.length > 0)\n ? $json.data.action_items.map(item => '\u2022 ' + item).join('\\n')\n : '(No action items captured.)'\n ) }},\n \"original_content_section\": {{ JSON.stringify($json.data.original_content || '(No source content available.)') }},\n \"doc_body\": {{ JSON.stringify(\n [\n $json.data.meeting_title || ('Meeting Notes \u2014 ' + ($json.data.meeting_date || $now.format('yyyy-MM-dd'))),\n '',\n 'Date: ' + ($json.data.meeting_date || $now.format('yyyy-MM-dd')),\n '',\n '\u2014 ATTENDEES \u2014',\n ($json.data.attendees && $json.data.attendees.length > 0)\n ? $json.data.attendees.map(a => '\u2022 ' + a).join('\\n')\n : '(none listed)',\n '',\n '\u2014 SUMMARY \u2014',\n ($json.data.summary && $json.data.summary.trim().length > 0) ? $json.data.summary : '(No additional summary captured.)',\n '',\n '\u2014 ACTION ITEMS \u2014',\n ($json.data.action_items && $json.data.action_items.length > 0)\n ? $json.data.action_items.map(item => '\u2022 ' + item).join('\\n')\n : '(No action items captured.)',\n '',\n '\u2014 SOURCE CONTENT (REFERENCE) \u2014',\n $json.data.original_content || '(No source content available.)'\n ].join('\\n')\n ) }},\n \"action_items_count\": {{ ($json.data.action_items || []).length }}\n}"
},
"typeVersion": 3.4
},
{
"id": "2b050eed-db76-40ff-bf7e-e68736a4cd56",
"name": "Google Docs: Create Doc",
"type": "n8n-nodes-base.googleDocs",
"position": [
1072,
672
],
"parameters": {
"title": "={{ $('Set: Build Doc Body').item.json.meeting_title }}",
"folderId": "YOUR_DRIVE_FOLDER_ID"
},
"typeVersion": 2
},
{
"id": "0039b954-057e-4562-ac71-f4ddd18591b5",
"name": "Google Docs: Insert Body",
"type": "n8n-nodes-base.googleDocs",
"position": [
1376,
672
],
"parameters": {
"actionsUi": {
"actionFields": [
{
"text": "={{ $('Set: Build Doc Body').item.json.doc_body }}",
"action": "insert"
}
]
},
"operation": "update",
"documentURL": "={{ $json.id }}"
},
"typeVersion": 2
},
{
"id": "58041c40-6498-46ba-8e72-965d80c9951a",
"name": "Gmail: Reply with Doc Link",
"type": "n8n-nodes-base.gmail",
"position": [
1680,
672
],
"parameters": {
"message": "=<p>Hi,</p>\n\n<p>Captured your meeting notes from <strong>\"{{ $('Set: Build Doc Body').item.json.meeting_title }}\"</strong>.</p>\n\n<p>\ud83d\udcc4 <a href=\"https://docs.google.com/document/d/{{ $('Google Docs: Create Doc').item.json.id }}/edit\">Open the meeting doc</a></p>\n\n<p>\n <strong>{{ $('Set: Build Doc Body').item.json.action_items_count }}</strong> action item(s) captured.\n</p>\n\n<p style=\"color: #888; font-size: 12px;\">Sent automatically by easybits Extractor.</p>",
"options": {
"appendAttribution": false
},
"messageId": "={{ $('Gmail: Watch Inbox').item.json.id }}",
"operation": "reply"
},
"typeVersion": 2.1
},
{
"id": "009f2f93-0d70-441a-8f28-29b262b8224d",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
64,
448
],
"parameters": {
"color": 7,
"width": 288,
"height": 400,
"content": "## \ud83d\udce7 Email Intake\nPolls Gmail every minute for unread emails with attachments. Designed for a dedicated inbox (e.g. `meetings@yourdomain.com`) \u2013 no labels or filters needed."
},
"typeVersion": 1
},
{
"id": "186f85ca-4278-4bc3-b20d-09861ff992db",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
368,
448
],
"parameters": {
"color": 7,
"width": 288,
"height": 400,
"content": "## \ud83d\udd0d Document Extraction\nSends the email's image attachment to the easybits Extractor and returns a structured object with meeting title, date, attendees, summary, action items, and a full reference transcription."
},
"typeVersion": 1
},
{
"id": "1c5692b0-bba6-4550-ac6a-662b15938c09",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
672,
448
],
"parameters": {
"color": 7,
"width": 288,
"height": 400,
"content": "## \u2699\ufe0f Build Doc Body\nAssembles the structured Extractor output into a single plain-text doc body, with sensible fallbacks for any fields that came back empty or null."
},
"typeVersion": 1
},
{
"id": "8511f454-b4aa-41e1-907a-cb1905c057f5",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
976,
448
],
"parameters": {
"color": 7,
"width": 288,
"height": 400,
"content": "## \ud83d\udcc4 Create Empty Doc\nCreates a new Google Doc in the designated Drive folder, named after the extracted meeting title (with a date-based fallback if no title was written on the page)."
},
"typeVersion": 1
},
{
"id": "00bd4aba-98c9-444a-ad28-83b825d42769",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1280,
448
],
"parameters": {
"color": 7,
"width": 288,
"height": 400,
"content": "## \u270f\ufe0f Fill the Doc\nInserts the pre-built body text into the empty doc as plain text. For styled output (real headings, bold), swap this node for an HTTP Request to the Google Docs `batchUpdate` API."
},
"typeVersion": 1
},
{
"id": "9a40f4e5-6112-408f-ba8a-5bcedc6537f5",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1584,
448
],
"parameters": {
"color": 7,
"width": 288,
"height": 400,
"content": "## \ud83d\udcec Send Confirmation\nReplies to the original email with the doc link and the action item count. Threading is preserved by replying to the original message ID."
},
"typeVersion": 1
},
{
"id": "f87331fe-6919-4fa8-8dd4-db4dc8f8ed3c",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-896,
-48
],
"parameters": {
"width": 944,
"height": 1424,
"content": "# \ud83d\udcdd Whiteboard to Meeting Doc\n(powered by easybits)\n\n## How It Works\nThis workflow turns photos of handwritten meeting notes into structured Google Docs. Email a photo of a whiteboard, notebook page, or napkin to a dedicated inbox. **easybits Extractor** reads the image and pulls out the meeting title, date, attendees, summary, action items, and a full reference transcription. The result is created as a new Google Doc in a designated Drive folder, and a confirmation email with the doc link is sent back to the sender.\n\n**Flow overview:**\n1. User emails a photo of meeting notes to a dedicated inbox\n2. Gmail Trigger picks up the email and downloads the attachment\n3. easybits Extractor reads the image and returns structured data\n4. A Set node builds the doc body with sensible fallbacks for missing fields\n5. A new Google Doc is created in the designated Drive folder\n6. The body text is inserted into the doc\n7. A confirmation email with the doc link is replied to the sender\n\n\n**Extracted fields:**\n`meeting_title` \u00b7 `meeting_date` \u00b7 `attendees` \u00b7 `summary` \u00b7 `action_items` \u00b7 `original_content`\n\n\n## Setup Guide\n### 1. Set Up Your easybits Extractor Pipeline\n1. Go to **extractor.easybits.tech** and create a new pipeline\n2. Add six fields to the response structure: **meeting_title**, **meeting_date**, **attendees** (string array), **summary**, **action_items** (string array), and **original_content**\n3. Write field descriptions that instruct the model to return null when handwriting is unclear, rather than guessing \u2013 this prevents hallucinated names and dates\n4. Copy your API key from the easybits dashboard\n\n### 2. Install the easybits Community Node\nThe node is **verified**, so it's available on n8n Cloud out of the box \u2013 just search for \"easybits Extractor\" in the node panel and start using it. No installation needed.\n\nIf you're on a **self-hosted instance**, go to **Settings \u2192 Community Nodes \u2192 Install** and enter: `@easybits/n8n-nodes-extractor`\n\n### 3. Connect a Dedicated Email Address\nConnect the Gmail Trigger to an email address used only for digitalizing meeting notes (e.g. `meetings@yourdomain.com`). Every email that arrives in this inbox will trigger the workflow \u2013 no labels or filters required. The workflow marks emails as read after processing to prevent re-triggering.\n\n### 4. Create a Destination Folder in Google Drive\n1. Create a folder for archived meeting docs (e.g. **Meeting Notes / 2026**)\n2. Open the folder and copy the folder ID from the URL (the long string at the end)\n3. Paste it into the **Folder** field of the **Google Docs: Create Doc** node\n\n### 5. Connect Credentials\n- **Gmail OAuth2** \u2192 for the trigger and reply nodes\n- **Google Docs OAuth2** \u2192 for the create and update nodes\n- **easybits Extractor API** \u2192 paste the API key from step 1, and select the pipeline you created\n\n### 6. Activate and Test\n1. Save and activate the workflow\n2. Send a test email to the dedicated inbox with a photo of meeting notes attached\n3. Within a minute, a new doc should appear in your Drive folder and a confirmation email should land in your inbox\n\n\n## Free Tier\neasybits gives you 50 extractions/month for free \u2013 plenty to test and run light personal use without paying anything."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"availableInMCP": false,
"executionOrder": "v1"
},
"connections": {
"Gmail: Watch Inbox": {
"main": [
[
{
"node": "easybits: Extract Meeting Notes",
"type": "main",
"index": 0
}
]
]
},
"Set: Build Doc Body": {
"main": [
[
{
"node": "Google Docs: Create Doc",
"type": "main",
"index": 0
}
]
]
},
"Google Docs: Create Doc": {
"main": [
[
{
"node": "Google Docs: Insert Body",
"type": "main",
"index": 0
}
]
]
},
"Google Docs: Insert Body": {
"main": [
[
{
"node": "Gmail: Reply with Doc Link",
"type": "main",
"index": 0
}
]
]
},
"easybits: Extract Meeting Notes": {
"main": [
[
{
"node": "Set: Build Doc Body",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow monitors a Gmail inbox for unread emails with image attachments, uses easybits Extractor to turn handwritten meeting notes into structured data, creates a Google Doc in a chosen Drive folder, inserts the formatted notes, and replies to the original email with the…
Source: https://n8n.io/workflows/15985/ — 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.
2025-12-03 fix JS code in node
Receive any business document via email. The attachment is automatically classified (Invoice, Contract, or Purchase Order) using easybits Extractor, then routed down the correct path where a second Ex
The goal is to reduce inbox noise and automatically organize repetitive types of emails so that imprtant messages remain visible while unsolicited or promotional emails are handled automatically. When
This template is built to be customized for your specific needs. This template has the core logic and n8n node specific references sorted to work with dynamic file names throughout the workflow. Store
This is an elite enterprise-grade solution for Talent Acquisition and HR Ops teams. It automates the high-volume task of resume screening by transforming unstructured PDF applications into structured