{
  "name": "Meeting Notes to Action Items (any team)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "*/30 6-20 * * *"
            },
            {
              "field": "cronExpression",
              "expression": "0 21 * * *"
            }
          ]
        }
      },
      "name": "Every 30 Min, 6am to 9pm",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        300
      ],
      "id": "f017a425-8c39-4b7e-88ab-7f11ded62969"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "c1c62a2a-3234-4fbe-8f0b-2aac5f392ab2",
              "name": "notesNameContains",
              "value": "Notes by Gemini",
              "type": "string"
            },
            {
              "id": "274f7fb1-cc9a-47c1-bdfb-2366e1481445",
              "name": "summaryTo",
              "value": "you@example.com",
              "type": "string"
            },
            {
              "id": "d16838f6-a3f7-4be1-8303-9149b84c330d",
              "name": "lookbackMinutes",
              "value": 1440,
              "type": "number"
            },
            {
              "id": "2632e896-e7c8-445d-8cbc-73d629680c05",
              "name": "dryRun",
              "value": true,
              "type": "boolean"
            }
          ]
        },
        "options": {}
      },
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        220,
        300
      ],
      "id": "dae76b7c-b97d-4faa-ac98-c94516902843"
    },
    {
      "parameters": {
        "url": "https://www.googleapis.com/drive/v3/files",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleDriveOAuth2Api",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "q",
              "value": "=name contains '{{ $json.notesNameContains }}' and mimeType = 'application/vnd.google-apps.document' and trashed = false and modifiedTime > '{{ $now.minus({minutes: $json.lookbackMinutes}).toUTC().toISO() }}'"
            },
            {
              "name": "fields",
              "value": "files(id,name,modifiedTime,webViewLink)"
            },
            {
              "name": "includeItemsFromAllDrives",
              "value": "true"
            },
            {
              "name": "supportsAllDrives",
              "value": "true"
            },
            {
              "name": "orderBy",
              "value": "modifiedTime desc"
            }
          ]
        },
        "options": {}
      },
      "name": "Find New Meeting Notes",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        440,
        300
      ],
      "id": "4fdaf059-38ce-46f5-a494-bc1f86c1f6bf"
    },
    {
      "parameters": {
        "fieldToSplitOut": "files",
        "options": {}
      },
      "name": "One Item per Note",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        660,
        300
      ],
      "id": "d8108574-a477-4b67-b206-06a50ba40a4a"
    },
    {
      "parameters": {
        "operation": "removeItemsRepeatedAcrossExecutions",
        "dedupeValue": "={{ $json.id }}",
        "options": {}
      },
      "name": "Skip Notes Already Read",
      "type": "n8n-nodes-base.removeDuplicates",
      "typeVersion": 2,
      "position": [
        880,
        300
      ],
      "id": "48be83be-64d5-41f5-a881-16e470278238"
    },
    {
      "parameters": {
        "url": "=https://www.googleapis.com/drive/v3/files/{{ $json.id }}/export",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleDriveOAuth2Api",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "mimeType",
              "value": "text/plain"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "responseFormat": "text",
              "outputPropertyName": "noteText"
            }
          }
        }
      },
      "name": "Get Note Text",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1100,
        300
      ],
      "id": "86973dc8-7ee9-43c4-a97a-235b8aa1ae18"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=You read meeting notes and pull out every action item someone on the call committed to.\n\nReturn ONLY a JSON array, no prose, no code fences. Each element:\n{\"title\": \"short imperative task name, under 10 words\",\n \"owner\": \"the person who said they would do it; first name is fine\",\n \"details\": \"one or two sentences of context from the notes\",\n \"due\": \"YYYY-MM-DD if a date was stated, otherwise empty string\",\n \"priority\": \"High\" | \"Normal\" | \"Low\"}\n\nRules:\n- Only things someone committed to. Ideas and maybes are not action items.\n- Every item has exactly one owner. If two people share it, name the one who will drive it.\n- If there are none, return [].\n\nMeeting: {{ $('One Item per Note').item.json.name }}\nNotes:\n{{ $json.noteText }}",
        "options": {}
      },
      "name": "Extract Every Action Item",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        1320,
        300
      ],
      "id": "66f5a7d6-c50b-4476-8608-7b35ced89df6"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-4-5-20250929",
          "cachedResultName": "Claude Sonnet 4.5"
        },
        "options": {}
      },
      "name": "Claude",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "typeVersion": 1.3,
      "position": [
        1320,
        520
      ],
      "id": "d904beea-f558-4adf-b7e5-15c3f88c21aa"
    },
    {
      "parameters": {
        "jsCode": "const out = [];\nfor (const [i, item] of $input.all().entries()) {\n  const note = $('One Item per Note').all()[i]?.json ?? {};\n  const raw = String(item.json.output ?? item.json.text ?? '').replace(/```(json)?/g, '').trim();\n  const start = raw.indexOf('['), end = raw.lastIndexOf(']');\n  let actions = [];\n  try { actions = JSON.parse(raw.slice(start, end + 1)); } catch (e) { actions = []; }\n  for (const a of actions) out.push({ json: { ...a, meeting: note.name, noteLink: note.webViewLink } });\n}\nreturn out;"
      },
      "name": "One Item per Action",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1540,
        300
      ],
      "id": "941f6674-9f91-48bc-b117-01cbe4c1dee4"
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list"
        },
        "sheetName": {
          "__rl": true,
          "value": "",
          "mode": "list"
        },
        "options": {}
      },
      "name": "Read the Action Items Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1760,
        300
      ],
      "id": "51e7080e-8e50-468c-9525-154f55799f91",
      "executeOnce": true,
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "jsCode": "// An item is already logged when an open row has the same owner and a near-identical task.\nconst rows = $('Read the Action Items Sheet').all().map(i => i.json).filter(r => r.Task && String(r.Status).toLowerCase() !== 'done');\nconst words = s => new Set(String(s).toLowerCase().replace(/[^a-z0-9 ]/g, ' ').split(/\\s+/).filter(w => w.length > 2));\nconst score = (a, b) => { const A = words(a), B = words(b); if (!A.size || !B.size) return 0;\n  let hit = 0; for (const w of A) if (B.has(w)) hit++; return hit / Math.min(A.size, B.size); };\nconst dryRun = $('Config').first().json.dryRun;\nreturn $('One Item per Action').all().map(({ json: a }) => {\n  const same = rows.find(r => String(r.Owner).toLowerCase() === String(a.owner).toLowerCase() && score(a.title, r.Task) >= 0.6);\n  return { json: { ...a, dryRun, alreadyLogged: Boolean(same), loggedAs: same?.Task ?? '' } };\n});"
      },
      "name": "Skip What Is Already Logged",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1980,
        300
      ],
      "id": "fcd12b94-8053-4689-a73d-f6370e02fbcf"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "7499a43b-eead-4878-82bd-2210ae983025",
              "leftValue": "={{ $json.dryRun }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "Dry Run?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2200,
        300
      ],
      "id": "90135d90-8880-4521-a5cc-41a99ba02844"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "0dd9be92-cd16-4df6-84ff-29b7de663f0c",
              "leftValue": "={{ $json.alreadyLogged }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "Only New Items",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.2,
      "position": [
        2420,
        420
      ],
      "id": "cf4b71ec-237e-481c-ad9b-206ed81a38e0"
    },
    {
      "parameters": {
        "numberInputs": 2
      },
      "name": "Gather Results",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [
        2860,
        300
      ],
      "id": "9f9dd424-6aee-4296-828d-020bab1dbb43"
    },
    {
      "parameters": {
        "jsCode": "const rows = $('Skip What Is Already Logged').all().map(i => i.json);\nif (!rows.length) return [];\nconst dry = rows[0].dryRun;\nconst byOwner = {};\nfor (const r of rows) (byOwner[r.owner || 'Unassigned'] ??= []).push(r);\nconst li = r => `<li>${r.title}${r.due ? ' (due ' + r.due + ')' : ''}${r.alreadyLogged ? ' <i>already on the sheet</i>' : ''}</li>`;\nconst meetings = [...new Set(rows.map(r => r.meeting))].join(', ');\nconst body = Object.entries(byOwner).map(([o, rs]) => `<p><b>${o}</b></p><ul>${rs.map(li).join('')}</ul>`).join('');\nreturn [{ json: {\n  subject: `${dry ? '[Dry run] ' : ''}Action items from ${meetings}`,\n  html: `<p>${dry ? 'Dry run: nothing was written to the sheet. Set dryRun to false in Config when this looks right.' : 'New items added to the sheet.'}</p>${body}`\n}}];"
      },
      "name": "Build Summary Email",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3080,
        300
      ],
      "id": "030576f4-ecec-45ec-952e-33ba74881f1f",
      "executeOnce": true
    },
    {
      "parameters": {
        "sendTo": "={{ $('Config').first().json.summaryTo }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false
        }
      },
      "name": "Email Me the Summary",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        3300,
        300
      ],
      "id": "2ae7727f-a662-4193-b6c4-93628789ba64"
    },
    {
      "parameters": {
        "content": "## Meeting notes to an action-item sheet\nEvery 30 minutes from 6am to 9pm this finds new meeting notes in your Drive, pulls out **every** action item and who owns it, and adds the new ones to a Google Sheet. One email after each meeting lists them by person.\n\n**Set up (15 min):**\n1. Make a Google Sheet with these headers in row 1: Logged, Meeting, Task, Owner, Due, Priority, Details, Notes Link, Status.\n2. Config: what your notes are called (Gemini's say \"Notes by Gemini\"), and where the summary goes.\n3. Connect Google Drive (both HTTP nodes), Google Sheets (pick your sheet in both Sheet nodes) and Gmail.\n4. Anthropic key on the Claude node.\n\n**Dry run is on.** It reads and writes nothing, then emails what it would have done. Switch dryRun to false when a week of emails looks right. Mark a row Done and it stops counting as open.",
        "height": 420,
        "width": 420,
        "color": 5
      },
      "name": "Read Me First",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -40,
        -200
      ],
      "id": "c4fec1f8-5d75-482f-ae20-0bbeca662a1c"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "",
          "mode": "list"
        },
        "sheetName": {
          "__rl": true,
          "value": "",
          "mode": "list"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Logged": "={{ $now.toFormat('yyyy-MM-dd HH:mm') }}",
            "Meeting": "={{ $json.meeting }}",
            "Task": "={{ $json.title }}",
            "Owner": "={{ $json.owner }}",
            "Due": "={{ $json.due }}",
            "Priority": "={{ $json.priority }}",
            "Details": "={{ $json.details }}",
            "Notes Link": "={{ $json.noteLink }}",
            "Status": "Open"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "Logged",
              "displayName": "Logged",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Meeting",
              "displayName": "Meeting",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Task",
              "displayName": "Task",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Owner",
              "displayName": "Owner",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Due",
              "displayName": "Due",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Priority",
              "displayName": "Priority",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Details",
              "displayName": "Details",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Notes Link",
              "displayName": "Notes Link",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "displayName": "Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ]
        },
        "options": {}
      },
      "name": "Add New Items to the Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        2640,
        520
      ],
      "id": "8e290395-2b3f-463f-8d17-8263aa3cdfb3"
    }
  ],
  "connections": {
    "Every 30 Min, 6am to 9pm": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "Find New Meeting Notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find New Meeting Notes": {
      "main": [
        [
          {
            "node": "One Item per Note",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "One Item per Note": {
      "main": [
        [
          {
            "node": "Skip Notes Already Read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip Notes Already Read": {
      "main": [
        [
          {
            "node": "Get Note Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Note Text": {
      "main": [
        [
          {
            "node": "Extract Every Action Item",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Every Action Item": {
      "main": [
        [
          {
            "node": "One Item per Action",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "One Item per Action": {
      "main": [
        [
          {
            "node": "Read the Action Items Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read the Action Items Sheet": {
      "main": [
        [
          {
            "node": "Skip What Is Already Logged",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Skip What Is Already Logged": {
      "main": [
        [
          {
            "node": "Dry Run?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude": {
      "ai_languageModel": [
        [
          {
            "node": "Extract Every Action Item",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Dry Run?": {
      "main": [
        [
          {
            "node": "Gather Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Only New Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Only New Items": {
      "main": [
        [
          {
            "node": "Add New Items to the Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add New Items to the Sheet": {
      "main": [
        [
          {
            "node": "Gather Results",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Gather Results": {
      "main": [
        [
          {
            "node": "Build Summary Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Summary Email": {
      "main": [
        [
          {
            "node": "Email Me the Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {
    "Find New Meeting Notes": [
      {
        "json": {
          "files": [
            {
              "id": "sample-doc-1",
              "name": "Weekly sync with Northwind Studio - Notes by Gemini",
              "modifiedTime": "2026-10-01T15:00:00Z",
              "webViewLink": "https://docs.google.com/document/d/sample-doc-1"
            }
          ]
        }
      }
    ],
    "Get Note Text": [
      {
        "json": {
          "noteText": "Notes by Gemini - Weekly sync with Northwind Studio (fictional)\nAttendees: Dana, Priya (Northwind), Marcus (Northwind)\n\nSummary\nReviewed the homepage redesign and the launch timeline.\n\nNext steps\n- Dana will send the revised homepage mockups to Priya by 2026-10-09.\n- Dana will set up the staging site for client review.\n- Marcus will confirm the final logo files.\n- Dana will follow up with Marcus if the logo files are not in by Friday.\n- Priya might want a blog section later (not decided)."
        }
      }
    ],
    "Read the Action Items Sheet": [
      {
        "json": {
          "Logged": "2026-09-28 10:00",
          "Meeting": "Kickoff",
          "Task": "Send revised homepage mockups",
          "Owner": "Dana",
          "Status": "Open"
        }
      }
    ]
  }
}