{
  "name": "Turn whiteboard photos into meeting notes and action items with Gemini",
  "nodes": [
    {
      "id": "50e436b8-aca4-4c56-a5f6-d1606b523ad1",
      "name": "Meeting Notes Form",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        112,
        48
      ],
      "parameters": {
        "options": {
          "buttonLabel": "Create notes",
          "respondWithOptions": {
            "values": {
              "formSubmittedText": "Thanks! Your notes are being generated and will arrive by email."
            }
          }
        },
        "formTitle": "Whiteboard to meeting notes",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Meeting title",
              "requiredField": true
            },
            {
              "fieldLabel": "Attendee emails",
              "placeholder": "user@example.com, user@example.com",
              "requiredField": true
            },
            {
              "fieldType": "file",
              "fieldLabel": "Whiteboard photo",
              "multipleFiles": false,
              "requiredField": true,
              "acceptFileTypes": ".jpg,.jpeg,.png"
            }
          ]
        },
        "formDescription": "Upload a photo of the whiteboard and get structured notes, decisions and action items by email."
      },
      "typeVersion": 2.6
    },
    {
      "id": "b137b7ed-bdb9-4b2c-a4cc-d3b436bb52fa",
      "name": "Read Whiteboard with Gemini",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "onError": "continueErrorOutput",
      "position": [
        448,
        48
      ],
      "parameters": {
        "text": "=You are a meeting scribe. Read the whiteboard photo and turn it into clean notes for the meeting \"{{ $json['Meeting title'] }}\". Extract a short summary, the decisions that were made, every action item with its owner and due date when written, and any open questions. If the handwriting is unclear, make your best reading and keep it short.",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "type": "HumanMessagePromptTemplate",
              "imageDetail": "high",
              "messageType": "imageBinary",
              "binaryImageDataKey": "Whiteboard_photo"
            }
          ]
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.9
    },
    {
      "id": "adafe22a-2a09-4ead-aea5-a5e22cbcd780",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        400,
        224
      ],
      "parameters": {
        "options": {
          "temperature": 0.2
        },
        "modelName": "models/gemini-3.1-flash-lite"
      },
      "typeVersion": 1.1
    },
    {
      "id": "ebbcd0f1-42e8-4684-b92f-544784fea329",
      "name": "Meeting Notes Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        640,
        224
      ],
      "parameters": {
        "jsonSchemaExample": "{\"summary\":\"Agreed the Q3 roadmap\",\"decisions\":[\"Ship billing in July\"],\"action_items\":[{\"task\":\"Draft billing spec\",\"owner\":\"Sam\",\"due\":\"2026-08-01\"}],\"open_questions\":[\"Who owns pricing?\"]}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "20f38f2a-ba6a-428c-990e-430fbcb46fe2",
      "name": "Parse Meeting Notes",
      "type": "n8n-nodes-base.code",
      "position": [
        1200,
        32
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const src = $json.output || $json;\nconst f = $('Meeting Notes Form').item.json;\nconst list = function (x) { return Array.isArray(x) ? x : []; };\nconst actions = list(src.action_items).map(function (a) { return { task: a.task || '', owner: a.owner || 'Unassigned', due: a.due || '' }; });\nreturn {\n  meetingTitle: f['Meeting title'] || '',\n  attendees: f['Attendee emails'] || '',\n  summary: src.summary || '',\n  decisions: list(src.decisions).join(' | '),\n  openQuestions: list(src.open_questions).join(' | '),\n  actionItems: actions,\n  actionCount: actions.length,\n  actionText: actions.map(function (a) { return '- ' + a.task + ' (' + a.owner + (a.due ? ', due ' + a.due : '') + ')'; }).join('\\n')\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "7a6d6ef1-1ad4-48ba-b98d-6a0178e50d69",
      "name": "Log Meeting Notes",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1440,
        32
      ],
      "parameters": {
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Meetings"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Select your spreadsheet"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "d1e79a50-3e28-491f-a814-7242f4037321",
      "name": "Email Notes to Attendees",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1680,
        32
      ],
      "parameters": {
        "sendTo": "={{ $(\"Parse Meeting Notes\").item.json.attendees }}",
        "message": "={{ \"Summary:\\n\" + $(\"Parse Meeting Notes\").item.json.summary + \"\\n\\nDecisions:\\n\" + $(\"Parse Meeting Notes\").item.json.decisions + \"\\n\\nAction items:\\n\" + $(\"Parse Meeting Notes\").item.json.actionText + \"\\n\\nOpen questions:\\n\" + $(\"Parse Meeting Notes\").item.json.openQuestions }}",
        "options": {},
        "subject": "={{ \"Meeting notes - \" + $(\"Parse Meeting Notes\").item.json.meetingTitle }}",
        "emailType": "text"
      },
      "typeVersion": 2.2
    },
    {
      "id": "186b98c0-cce9-47ef-975c-6e75d33f4156",
      "name": "Has Action Items",
      "type": "n8n-nodes-base.if",
      "position": [
        1968,
        32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $(\"Parse Meeting Notes\").item.json.actionCount }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "fb5f149f-1f9c-40f0-8619-c7636a30636b",
      "name": "Split Action Items",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        2272,
        -128
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "actionItems"
      },
      "typeVersion": 1
    },
    {
      "id": "e6f9dd4b-d499-42f6-bd47-fd72f861e5fe",
      "name": "Log Action Items",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2464,
        -128
      ],
      "parameters": {
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Action Items"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "Select your spreadsheet"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "92019f79-1f47-4e95-bd02-ddd00a9a420e",
      "name": "Email Read Failure",
      "type": "n8n-nodes-base.gmail",
      "position": [
        864,
        256
      ],
      "parameters": {
        "sendTo": "={{ $(\"Meeting Notes Form\").item.json[\"Attendee emails\"] }}",
        "message": "We could not read the uploaded whiteboard photo. Please try again with a clearer, well-lit picture.",
        "options": {},
        "subject": "={{ \"Could not read the whiteboard - \" + $(\"Meeting Notes Form\").item.json[\"Meeting title\"] }}",
        "emailType": "text"
      },
      "typeVersion": 2.2
    },
    {
      "id": "dfbd0db5-11dc-4a24-9a71-56b5b52947a4",
      "name": "Overview Sticky",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -560,
        -288
      ],
      "parameters": {
        "width": 536,
        "height": 692,
        "content": "## Turn whiteboard photos into meeting notes and action items with Gemini\n\n### How it works\nThe meeting ends, someone photographs the whiteboard, and the notes never get written. Submit the photo through the built-in n8n form and a Basic LLM Chain with Google Gemini reads the handwriting directly from the image, returning a summary, the decisions taken, every action item with its owner and due date, and any open questions. The meeting is logged to Google Sheets, the notes are emailed to all attendees, and when action items exist they are split out and appended one row each to an Action Items sheet you can sort and filter. If the photo is unreadable the submitter gets an email asking for a clearer shot.\n\n### Setup\n1. Connect Google Gemini (PaLM) API, Google Sheets and Gmail.\n2. Point the two Sheets nodes at your spreadsheet (tabs: Meetings, Action Items).\n3. Open the form URL on your phone and submit a whiteboard photo.\n\n### Customization tips\nSwap the Action Items sheet for Notion or Trello, or add a Slack post for the team channel."
      },
      "typeVersion": 1
    },
    {
      "id": "c19922e0-9abd-43c5-8198-a1c01f28dcf2",
      "name": "S1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 332,
        "height": 708,
        "content": "## 1. Submit the photo\nA built-in n8n form takes the meeting title, attendees and the whiteboard photo. No external service needed."
      },
      "typeVersion": 1
    },
    {
      "id": "606298fe-f6e1-4811-bdfa-3735c4a8fb81",
      "name": "S2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        352,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 740,
        "height": 708,
        "content": "## 2. AI reads the image (Basic LLM Chain)\nGemini reads the handwriting straight from the photo and returns structured JSON. Unreadable photos trigger an email back to the submitter."
      },
      "typeVersion": 1
    },
    {
      "id": "2fcbb39e-d1f9-4415-a9ed-86ea3a33f1d9",
      "name": "S3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1120,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 752,
        "height": 708,
        "content": "## 3. Log & send the notes\nAppend the meeting to Sheets and email the summary, decisions, actions and open questions to every attendee."
      },
      "typeVersion": 1
    },
    {
      "id": "03b2d1d9-97f0-4be9-b355-1bf50dc04d3f",
      "name": "S4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1888,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 740,
        "height": 708,
        "content": "## 4. Fan out the action items\nWhen the board contained tasks, split them into one row per action so owners and due dates are trackable."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Has Action Items": {
      "main": [
        [
          {
            "node": "Split Action Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Meeting Notes": {
      "main": [
        [
          {
            "node": "Email Notes to Attendees",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Meeting Notes Form": {
      "main": [
        [
          {
            "node": "Read Whiteboard with Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Action Items": {
      "main": [
        [
          {
            "node": "Log Action Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Meeting Notes": {
      "main": [
        [
          {
            "node": "Log Meeting Notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Meeting Notes Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Read Whiteboard with Gemini",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Email Notes to Attendees": {
      "main": [
        [
          {
            "node": "Has Action Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Read Whiteboard with Gemini",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Read Whiteboard with Gemini": {
      "main": [
        [
          {
            "node": "Parse Meeting Notes",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Email Read Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false
}
