{"workflow":{"id":13004,"name":"Extract structured data from Gmail attachments to Google Sheets, GPT vision","views":119,"recentViews":0,"totalViews":119,"createdAt":"2026-01-26T08:20:55.344Z","description":"Automatically extract structured information from emails using AI-powered document analysis. This workflow processes emails from specified domains, classifies them by type, and extracts structured data from various attachment formats.\n\n## Who is this for\nOperations teams, coordinators, and business professionals who receive proposals or reports from multiple sources via email and need to consolidate the information into a structured database.\n\n## What this workflow does\n1. **Monitors Gmail** every 30 minutes for emails from specified domains\n2. **Classifies emails** into three categories based on customizable keywords\n3. **Processes attachments** intelligently based on file type and email classification\n4. **Extracts structured data**: dates, times, names, amounts, and other fields\n5. **Saves to Google Sheets** with full metadata and classification\n6. **Labels processed emails** in Gmail for tracking\n\n## Setup requirements\n- Gmail OAuth2 credentials\n- OpenAI API key (GPT-4 Vision)\n- Google Sheets OAuth2 credentials\n- AWS S3 bucket for temporary image storage\n- ConvertAPI account for PPTX/PDF conversion\n\n## How to customize\nEdit the domain list and classification keywords in the code nodes to adapt for your specific use case.","workflow":{"meta":{"instanceId":"template-instance-id","templateCredsSetupCompleted":true},"name":"Extract structured data from Gmail attachments to Google Sheets using GPT-4o Vision","nodes":[{"id":"762adf7a-01c4-44f8-800b-b084812cf0c3","name":"Workflow Description","type":"n8n-nodes-base.stickyNote","position":[2384,1216],"parameters":{"width":380,"height":776,"content":"##  Email Attachment Processor Template\n\nAutomatically extract structured information from emails using AI.\n\n### What this workflow does:\n1. **Monitors Gmail** for emails from specified domains\n2. **Classifies emails** into custom categories\n3. **Processes attachments** (Excel, PPTX, PDF, images) using GPT Vision\n4. **Extracts structured data** based on your schema\n5. **Saves to Google Sheets** with classification\n6. **Labels processed emails** in Gmail\n7. **Sends to custom API** (optional)\n8. **Notifies via Slack** (optional)\n\n### Requirements:\n- Gmail OAuth2 credentials\n- OpenAI API key (for GPT-4 Vision)\n- Google Sheets OAuth2 credentials\n- AWS S3 bucket (for image processing)\n- ConvertAPI account (for PPTX/PDF conversion)\n- Slack OAuth2 (optional)\n\n### Setup:\n1. Configure all credentials\n2. Update Google Sheets document ID in Variables\n3. Update S3 bucket name in Variables\n4. Customize domain filter in 'Domain Filter' node\n5. Customize classification keywords as needed"},"typeVersion":1},{"id":"1a385d9d-4e9a-4a56-beb3-4d3e7dd9649b","name":"Email Classification Info","type":"n8n-nodes-base.stickyNote","position":[3040,1184],"parameters":{"color":7,"width":332,"height":336,"content":"###  Email Type Detection\n\n**Customize keywords for your use case:**\n\n**Category A (e.g., Cancellation):**\n- Add your keywords here\n\n**Category B (e.g., Updates):**\n- Add your keywords here\n\n**Category C (e.g., New Items):**\n- Default category\n\nCustomize keywords in the 'Domain Filter' code node."},"typeVersion":1},{"id":"579e95c7-8c34-4446-a229-061652df9808","name":"Gmail Labels Info","type":"n8n-nodes-base.stickyNote","position":[6560,1264],"parameters":{"color":3,"width":280,"height":216,"content":"###  Gmail Labels\n\nProcessed emails are labeled:\n- Processed_CategoryA\n- Processed_CategoryB\n- Processed_CategoryC\n\nLabels are created automatically if they don't exist.\n\n**Customize label names in the code nodes.**"},"typeVersion":1},{"id":"e852a330-e3ae-4b4f-b887-eec29afafa25","name":"File Processing Info","type":"n8n-nodes-base.stickyNote","position":[3696,1152],"parameters":{"color":7,"width":300,"height":344,"content":"###  File Processing Routes\n\n**Excel files (Category C):**\n→ Direct data extraction\n\n**Excel files (Category A/B):**\n→ GPT Vision analysis\n\n**PPTX/PDF files:**\n→ Convert to images → GPT Vision\n\n**Image attachments:**\n→ Direct GPT Vision analysis\n\n**Text-only emails:**\n→ GPT text analysis"},"typeVersion":1},{"id":"eb599c2d-491a-4824-85b9-a503a4b65c47","name":"ConvertAPI Info","type":"n8n-nodes-base.stickyNote","position":[4176,1760],"parameters":{"color":3,"width":300,"height":284,"content":"###  ConvertAPI Setup\n\nUsed for converting PPTX/PDF to images for GPT Vision analysis.\n\nhttps://www.convertapi.com\n\n*This service is paid but offers free initial conversions.*\n\nReplace the HTTP Header Auth credential with your ConvertAPI secret."},"typeVersion":1},{"id":"5b69b7ca-b4e1-42d3-ab8b-cfb915f745a1","name":"Google Sheets Info","type":"n8n-nodes-base.stickyNote","position":[6128,1136],"parameters":{"color":3,"width":300,"height":368,"content":"###  Google Sheets Output\n\nExtracted data is saved with columns:\n- email_type (Category)\n- field_1 (Date)\n- field_2 (Time)\n- field_3 (Name)\n- field_4 (Amount)\n- And more...\n\n**Customize the output schema in the 'Parse All Results' node.**\n\n**Update the Document ID** in Variables or directly in the Google Sheets node."},"typeVersion":1},{"id":"9a32ec51-62fb-49c8-9941-60bf6a1adc13","name":"AWS S3 Info","type":"n8n-nodes-base.stickyNote","position":[4208,1024],"parameters":{"color":7,"width":300,"height":288,"content":"###  AWS S3 Setup\n\nImages are temporarily uploaded to S3 for GPT Vision processing.\n\n**Required:**\n1. Create an S3 bucket\n2. Configure AWS credentials in n8n\n3. Update bucket name in Variables\n\nImages are used for GPT Vision API calls and can be cleaned up periodically."},"typeVersion":1},{"id":"61bd3fd0-9fdd-4cf0-9edf-28721a2358da","name":"Schedule Trigger","type":"n8n-nodes-base.scheduleTrigger","position":[2832,1536],"parameters":{"rule":{"interval":[{"field":"minutes","minutesInterval":30}]}},"typeVersion":1.2},{"id":"11ce6b59-1436-4ef5-a0ef-71f9aa98c763","name":"Gmail - Get Emails","type":"n8n-nodes-base.gmail","position":[3040,1536],"webhookId":"6fc06b6a-65bc-4bb7-adb6-d0ce5dc474a0","parameters":{"limit":1,"filters":{"q":"-label:Processed_CategoryA -label:Processed_CategoryB -label:Processed_CategoryC","receivedAfter":"={{ $now.minus({ days: 60 }).toISO() }}"},"operation":"getAll"},"typeVersion":2.1},{"id":"a6dff01e-96d5-4c57-8def-5bd8c1216c26","name":"Domain Filter","type":"n8n-nodes-base.code","position":[3264,1536],"parameters":{"mode":"runOnceForEachItem","jsCode":"// ============================================================\n// DOMAIN FILTER & EMAIL CLASSIFICATION\n// ============================================================\n// Customize this node for your specific use case\n// ============================================================\n\nconst json = $input.item.json;\nconst subject = json.subject || json.Subject || '';\nconst snippet = json.snippet || '';\nconst fromRaw = json.from || json.From || '';\nconst fromText = typeof fromRaw === 'object' ? (fromRaw.text || JSON.stringify(fromRaw)) : String(fromRaw);\nconst text = (subject + ' ' + snippet + ' ' + fromText).toLowerCase();\n\n// ============================================================\n// STEP 1: Configure your allowed domains\n// ============================================================\n// Add the domains you want to monitor (without @ symbol)\n// Example: ['company.com', 'partner.co.jp', 'supplier.net']\nconst allowedDomains = [\n  // Add your domains here\n  // 'example.com',\n  // 'example.co.jp',\n];\n\n// Check if email is from an allowed domain\nconst fromLower = fromText.toLowerCase();\nconst isFromAllowedDomain = allowedDomains.length === 0 || allowedDomains.some(d => fromLower.includes(d));\n\n// ============================================================\n// STEP 2: Configure classification keywords\n// ============================================================\n\n// Category A keywords (e.g., Cancellations, Deletions)\nconst categoryAKeywords = [\n  // Add your Category A keywords here\n  // 'cancel', 'cancelled', 'delete', 'remove'\n];\n\n// Category B keywords (e.g., Updates, Changes)\nconst categoryBKeywords = [\n  // Add your Category B keywords here\n  // 'update', 'change', 'modify', 'reschedule'\n];\n\n// Category C keywords (e.g., New items - this is the default)\nconst categoryCKeywords = [\n  // Add your Category C keywords here (optional)\n  // 'new', 'offer', 'proposal', 'available'\n];\n\n// ============================================================\n// STEP 3: Configure identifier codes (optional)\n// ============================================================\n// If your emails contain specific codes like [ABC] or 【XYZ】\nconst identifierCodes = [\n  // Add your identifier codes here (lowercase)\n  // 'abc', 'xyz', 'code1'\n];\n\n// Normalize full-width characters to ASCII\nconst normalizeAscii = value => String(value || '').replace(/[Ａ-Ｚ０-９]/g, ch => String.fromCharCode(ch.charCodeAt(0) - 0xFEE0));\nconst textUpper = normalizeAscii(subject + ' ' + snippet + ' ' + fromText).toUpperCase();\n\n// Check for identifier codes in brackets\nconst hasIdentifierCode = identifierCodes.some(c => \n  textUpper.includes('【' + c.toUpperCase() + '】') || \n  textUpper.includes('[' + c.toUpperCase() + ']')\n);\n\n// ============================================================\n// Classification Logic\n// ============================================================\n\n// Skip if not from allowed domain and no identifier match\nif (!isFromAllowedDomain && !hasIdentifierCode) {\n  return { json: { _skip: true } };\n}\n\n// Check if email matches any category keywords\nconst isCategoryA = categoryAKeywords.some(k => text.includes(k.toLowerCase()));\nconst isCategoryB = categoryBKeywords.some(k => text.includes(k.toLowerCase()));\nconst hasCategoryC = categoryCKeywords.length === 0 || categoryCKeywords.some(k => text.includes(k.toLowerCase()));\n\n// If no keywords configured and domain doesn't match, skip\nif (categoryAKeywords.length === 0 && categoryBKeywords.length === 0 && categoryCKeywords.length === 0) {\n  // Process all emails from allowed domains when no keywords configured\n  if (!isFromAllowedDomain) {\n    return { json: { _skip: true } };\n  }\n}\n\n// Determine email type/category\nlet emailType = 'CategoryC'; // Default\nif (isCategoryA) emailType = 'CategoryA';\nelse if (isCategoryB) emailType = 'CategoryB';\n\nreturn { json: { ...json, _skip: false, _email_type: emailType } };"},"typeVersion":2},{"id":"e303d6d7-5c68-4fc6-97d4-4e5e6c171472","name":"Filter Valid Emails","type":"n8n-nodes-base.filter","position":[3488,1536],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"operator":{"type":"boolean","operation":"notEquals"},"leftValue":"={{ $json._skip }}","rightValue":true}]}},"typeVersion":2},{"id":"6a426693-2bfc-4d4a-a17a-9f89973f9dc9","name":"Gmail - Get Attachments","type":"n8n-nodes-base.gmail","position":[3712,1536],"webhookId":"469deb7c-a92c-4140-9ab0-ae5e4f8f96ea","parameters":{"simple":false,"options":{"downloadAttachments":true,"dataPropertyAttachmentsPrefixName":"attachment_"},"messageId":"={{ $json.id }}","operation":"get"},"typeVersion":2.1},{"id":"7ce1026b-c28f-4e63-95fc-8dcb61814c3f","name":"Detect File Types","type":"n8n-nodes-base.code","position":[3920,1536],"parameters":{"mode":"runOnceForEachItem","jsCode":"// ============================================================\n// FILE TYPE DETECTION\n// ============================================================\n// Identifies attachment types and preserves metadata\n// ============================================================\n\nconst item = $input.item;\nconst binary = item.binary || {};\nconst json = item.json;\n\nconst subject = json.Subject || json.subject || '';\nconst fromData = json.From || json.from || '';\n\nlet emailType = json._email_type || '';\nif (!emailType) {\n  try {\n    const currentId = json.id;\n    const filteredItems = $('Filter Valid Emails').all();\n    for (const f of filteredItems) {\n      if (f.json.id === currentId && f.json._email_type) {\n        emailType = f.json._email_type;\n        break;\n      }\n    }\n  } catch(e) {}\n}\nemailType = emailType || 'CategoryC';\n\nlet fileInfo = { excel: null, pptx: null, pdf: null, image: null };\nlet attachments = [];\n\nfor (const key of Object.keys(binary)) {\n  const bin = binary[key];\n  if (!bin || !bin.fileName) continue;\n  const fn = bin.fileName.toLowerCase();\n  const mime = (bin.mimeType || '').toLowerCase();\n  attachments.push({ filename: bin.fileName, mimeType: bin.mimeType, key: key });\n  \n  if (fn.endsWith('.xlsx') || fn.endsWith('.xls')) fileInfo.excel = { key, fileName: bin.fileName };\n  else if (fn.endsWith('.pptx') || fn.endsWith('.ppt')) fileInfo.pptx = { key, fileName: bin.fileName };\n  else if (fn.endsWith('.pdf') || mime.includes('pdf')) fileInfo.pdf = { key, fileName: bin.fileName };\n  else if (fn.endsWith('.png') || fn.endsWith('.jpg') || fn.endsWith('.jpeg') || mime.startsWith('image/')) fileInfo.image = { key, fileName: bin.fileName, mimeType: bin.mimeType || 'image/png' };\n}\n\n// Variable format types need GPT Vision even for Excel\nconst isVariableFormat = emailType === 'CategoryA' || emailType === 'CategoryB';\nconst hasExcelDirect = fileInfo.excel !== null && !isVariableFormat;\nconst hasFileConvert = fileInfo.pptx !== null || fileInfo.pdf !== null || (fileInfo.excel !== null && isVariableFormat);\n\nlet convType = null, convKey = null;\nif (fileInfo.pptx) { convType = 'pptx'; convKey = fileInfo.pptx.key; }\nelse if (fileInfo.pdf) { convType = 'pdf'; convKey = fileInfo.pdf.key; }\nelse if (fileInfo.excel && isVariableFormat) { convType = 'xlsx'; convKey = fileInfo.excel.key; }\n\nconst output = {\n  ...json,\n  id: json.id,\n  Subject: subject,\n  subject: subject,\n  From: fromData,\n  from: fromData,\n  attachments: attachments,\n  has_excel: hasExcelDirect,\n  has_pptx_pdf: hasFileConvert,\n  has_image: fileInfo.image !== null,\n  excel_binary_key: fileInfo.excel ? fileInfo.excel.key : null,\n  conversion_type: convType,\n  conversion_binary_key: convKey,\n  image_binary_key: fileInfo.image ? fileInfo.image.key : null,\n  _email_type: emailType\n};\n\nreturn {\n  json: output,\n  binary: item.binary\n};"},"typeVersion":2},{"id":"64aef15e-2374-4845-acc6-0dfe45d35c8f","name":"IF - Has Excel?","type":"n8n-nodes-base.if","position":[4144,1536],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"operator":{"type":"boolean","operation":"equals"},"leftValue":"={{ $json.has_excel }}","rightValue":true}]}},"typeVersion":2},{"id":"7c133bed-216b-49fe-a6d9-8e7292391f87","name":"Extract from Excel","type":"n8n-nodes-base.extractFromFile","position":[4368,1344],"parameters":{"options":{},"operation":"xlsx","binaryPropertyName":"={{ $json.excel_binary_key || 'attachment_0' }}"},"typeVersion":1},{"id":"ae047edd-7921-47cd-84c1-98f9fd5b3ae2","name":"Parse Excel Data","type":"n8n-nodes-base.code","position":[5008,1216],"parameters":{"jsCode":"// ============================================================\n// EXCEL DATA EXTRACTION\n// ============================================================\n// Customize this node to parse your specific Excel format\n// ============================================================\n\n// Map codes to full names (customize for your use case)\nconst codeToNameMap = {\n  // 'CODE1': 'Full Name 1',\n  // 'CODE2': 'Full Name 2',\n};\n\nfunction excelTimeToHHMM(fraction) {\n  if (!fraction && fraction !== 0) return '';\n  const totalMinutes = Math.round(fraction * 24 * 60);\n  const hours = Math.floor(totalMinutes / 60);\n  const minutes = totalMinutes % 60;\n  return String(hours).padStart(2, '0') + ':' + String(minutes).padStart(2, '0');\n}\n\nlet excelSourceItems = [];\ntry { excelSourceItems = $('IF - Has Excel?').all(); } catch(e) {}\n\nconst allRows = $input.all().map(i => i.json);\nconst results = [];\n\nlet hintYear = new Date().getFullYear();\nlet hintMonth = new Date().getMonth() + 1;\nlet currentName = '';\nlet currentType = 'Regular';\n\nconst dataBySource = {};\n\nfor (const row of allRows) {\n  const allText = JSON.stringify(row);\n  const firstKey = Object.keys(row)[0] || '';\n  const firstVal = row[firstKey] || '';\n  \n  // Extract year/month from text (customize pattern as needed)\n  const ymMatch = allText.match(/(20\\d{2})[-\\/年]\\s*(\\d{1,2})/);\n  if (ymMatch) {\n    hintYear = parseInt(ymMatch[1]);\n    hintMonth = parseInt(ymMatch[2]);\n  }\n  \n  // Extract source name from brackets (customize pattern as needed)\n  const nameMatch = allText.match(/【([A-Z]+)[^】]*】/i);\n  if (nameMatch) {\n    const code = nameMatch[1].toUpperCase();\n    currentName = codeToNameMap[code] || code;\n  }\n  \n  // Check for codes in the text\n  for (const code of Object.keys(codeToNameMap)) {\n    if (allText.toUpperCase().includes(code)) {\n      currentName = codeToNameMap[code] || code;\n      break;\n    }\n  }\n  \n  // Skip header rows (customize as needed)\n  if (allText.includes('Header1') && allText.includes('Header2')) continue;\n\n  // ============================================================\n  // CUSTOMIZE: Add your Excel parsing logic here\n  // ============================================================\n  \n  // Example: Parse rows with specific structure\n  if (row.__EMPTY_1 !== undefined && typeof row.__EMPTY_1 === 'number') {\n    const startTime = typeof row.__EMPTY_2 === 'number'\n      ? excelTimeToHHMM(row.__EMPTY_2)\n      : excelTimeToHHMM(row.__EMPTY_1);\n    const endTime = typeof row.__EMPTY_4 === 'number'\n      ? excelTimeToHHMM(row.__EMPTY_4)\n      : excelTimeToHHMM(row.__EMPTY_2);\n\n    if (startTime) {\n      const sourceName = currentName || 'Unknown';\n      const amount = typeof row.__EMPTY_7 === 'number' ? row.__EMPTY_7 : (row.__EMPTY_5 || 0);\n      const dataItem = {\n        date: hintYear + '-' + String(hintMonth).padStart(2,'0') + '-' + String(row.__EMPTY_1).padStart(2,'0'),\n        day_of_week: firstVal,\n        start_time: startTime,\n        end_time: endTime,\n        amount: amount,\n        type: currentType,\n        name: '',\n        source_name: sourceName\n      };\n      if (!dataBySource[sourceName]) dataBySource[sourceName] = [];\n      dataBySource[sourceName].push(dataItem);\n    }\n  }\n}\n\n// Build results\nfor (const srcItem of excelSourceItems) {\n  const meta = srcItem.json;\n  const emailId = meta.id || '';\n  const emailType = meta._email_type || 'CategoryC'; \n  \n  let defaultSource = '';\n  const subjectText = (meta.Subject || meta.subject || '');\n  const sourceMatch = subjectText.match(/【([A-Za-z]+)】/i);\n  if (sourceMatch) defaultSource = codeToNameMap[sourceMatch[1].toUpperCase()] || sourceMatch[1].toUpperCase();\n\n  let dataForEmail = dataBySource[defaultSource] || [];\n\n  if (!defaultSource && dataForEmail.length == 0) {\n    const allSources = Object.keys(dataBySource);\n    if (allSources.length === 1) {\n      dataForEmail = dataBySource[allSources[0]] || [];\n    } else if (allSources.length > 1) {\n      dataForEmail = allSources.flatMap(name => dataBySource[name] || []);\n    }\n  }\n\n  results.push({\n    json: {\n      _meta_subject: meta.Subject || meta.subject,\n      _meta_from: meta.from || meta.From,\n      _meta_id: emailId,\n      email_id: emailId, \n      _meta_source_name: defaultSource,\n      _email_type: emailType,\n      choices: [{ message: { content: JSON.stringify(dataForEmail.length ? dataForEmail : [{notes: '⚠️No data found'}]) } }]\n    }\n  });\n}\n\nreturn results.length ? results : [{ json: { _no_data: true } }];"},"typeVersion":2},{"id":"edbfd491-dbab-411d-a3d2-c674ee34020d","name":"IF - Has PPTX/PDF?","type":"n8n-nodes-base.if","position":[4368,1536],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"operator":{"type":"boolean","operation":"equals"},"leftValue":"={{ $json.has_pptx_pdf }}","rightValue":true}]}},"typeVersion":2},{"id":"49b24d00-f787-4b0d-8162-d41f62f6ca0d","name":"ConvertAPI to PNG","type":"n8n-nodes-base.httpRequest","position":[4592,1488],"parameters":{"url":"=https://v2.convertapi.com/convert/{{ $json.conversion_type }}/to/png","method":"POST","options":{},"sendBody":true,"contentType":"multipart-form-data","authentication":"genericCredentialType","bodyParameters":{"parameters":[{"name":"File","parameterType":"formBinaryData","inputDataFieldName":"={{ $json.conversion_binary_key }}"}]},"genericAuthType":"httpHeaderAuth"},"typeVersion":4.2},{"id":"bdd00767-8b17-4d7f-bc38-588ba5a923b9","name":"Prepare for S3 Upload","type":"n8n-nodes-base.code","position":[4800,1488],"parameters":{"jsCode":"// ============================================================\n// PREPARE IMAGES FOR S3 UPLOAD\n// ============================================================\n\nconst allItems = $input.all();\nconst results = [];\n\nlet pptxPdfItems = [];\ntry {\n  const allFileItems = $('Detect File Types').all();\n  pptxPdfItems = allFileItems.filter(item => item.json.has_pptx_pdf === true);\n} catch(e) {}\n\nlet filterItems = [];\ntry { filterItems = $('Filter Valid Emails').all(); } catch(e) {}\n\nfor (let idx = 0; idx < allItems.length; idx++) {\n  const item = allItems[idx];\n  const response = item.json;\n  \n  let emailId='', subject='', fromData='', attachments=[], emailType='';\n  \n  if (pptxPdfItems[idx]) {\n    const meta = pptxPdfItems[idx].json;\n    emailId = meta.id || '';\n    subject = meta.Subject || '';\n    fromData = meta.From || '';\n    attachments = meta.attachments || [];\n    emailType = meta._email_type || '';\n  }\n  \n  if (!emailType && emailId) {\n    for (const f of filterItems) {\n      if (f.json.id === emailId && f.json._email_type) {\n        emailType = f.json._email_type;\n        break;\n      }\n    }\n  }\n  emailType = emailType || 'CategoryC';\n  \n  if (response.Files && response.Files.length > 0) {\n    const maxPages = Math.min(response.Files.length, 15);\n    const ts = Date.now();\n    \n    for (let i = 0; i < maxPages; i++) {\n      const fileData = response.Files[i];\n      if (fileData && fileData.FileData) {\n        const fileName = 'convert-' + emailId + '-page-' + (i+1) + '-' + ts + '.png';\n        results.push({\n          json: {\n            _meta_subject: subject,\n            _meta_from: fromData,\n            _meta_id: emailId,\n            _meta_attachments: attachments,\n            _meta_process_type: 'pptx_pdf',\n            _email_type: emailType,\n            _page_index: i,\n            _page_filename: fileName,\n            _total_pages: maxPages\n          },\n          binary: {\n            data: {\n              data: fileData.FileData,\n              mimeType: 'image/png',\n              fileName: fileName,\n              fileExtension: 'png'\n            }\n          }\n        });\n      }\n    }\n  }\n}\n\nif (results.length === 0) return [{ json: { _skip: true } }];\nreturn results;"},"typeVersion":2},{"id":"9a844a79-cb50-439f-abcf-ea9ef20d3510","name":"S3 Upload (PPTX)","type":"n8n-nodes-base.awsS3","position":[5024,1488],"parameters":{"fileName":"={{ $json._page_filename }}","operation":"upload","bucketName":"={{ $vars.S3_BUCKET_NAME || 'your-bucket-name' }}","additionalFields":{}},"typeVersion":2},{"id":"22f24843-db51-4bc5-aac7-bf9cf8034222","name":"Collect S3 URLs","type":"n8n-nodes-base.code","position":[5248,1488],"parameters":{"jsCode":"// ============================================================\n// COLLECT S3 URLs FOR GPT VISION\n// ============================================================\n\nlet prepItems = [];\ntry { prepItems = $('Prepare for S3 Upload').all(); } catch(e) {}\n\nconst bucket = $vars.S3_BUCKET_NAME || 'your-bucket-name';\nconst region = $vars.AWS_REGION || 'us-east-1';\nconst emailGroups = new Map();\n\nfor (const prep of prepItems) {\n  const p = prep.json;\n  const emailId = p._meta_id || '';\n  const filename = p._page_filename || '';\n  if (!emailId || !filename) continue;\n  \n  const s3Url = 'https://' + bucket + '.s3.' + region + '.amazonaws.com/' + filename;\n  \n  if (!emailGroups.has(emailId)) {\n    emailGroups.set(emailId, {\n      subject: p._meta_subject || '',\n      from: p._meta_from || '',\n      emailId: emailId,\n      attachments: p._meta_attachments || [],\n      emailType: p._email_type || 'CategoryC',\n      sourceName: p._meta_source_name || '',\n      urls: []\n    });\n  }\n  emailGroups.get(emailId).urls.push({ url: s3Url, index: p._page_index || 0 });\n}\n\nconst results = [];\nfor (const [emailId, data] of emailGroups) {\n  data.urls.sort((a, b) => a.index - b.index);\n  results.push({\n    json: {\n      _meta_subject: data.subject,\n      _meta_from: data.from,\n      _meta_id: data.emailId,\n      _meta_attachments: data.attachments,\n      _meta_source_name: data.sourceName,\n      _email_type: data.emailType,\n      _s3_urls: data.urls.map(u => u.url)\n    }\n  });\n}\n\nif (results.length === 0) return [{ json: { _skip: true } }];\nreturn results;"},"typeVersion":2},{"id":"f92643b3-d9a2-4053-b2ca-8f76294e0e67","name":"Prepare GPT Request (PPTX)","type":"n8n-nodes-base.code","position":[5472,1488],"parameters":{"mode":"runOnceForEachItem","jsCode":"// ============================================================\n// PREPARE GPT VISION REQUEST FOR PPTX/PDF\n// ============================================================\n// Customize the system prompt and extraction fields\n// ============================================================\n\n// Map codes to full names (customize for your use case)\nconst codeToNameMap = {\n  // 'CODE1': 'Full Name 1',\n  // 'CODE2': 'Full Name 2',\n};\n\nconst item = $input.item.json;\nconst subject = item._meta_subject || '';\nconst emailId = item._meta_id || '';\nconst s3Urls = item._s3_urls || [];\nconst emailType = item._email_type || 'CategoryC';\n\n// Extract source name from subject\nconst sourceMatch = subject.match(/【([A-Za-z]+)】/i);\nconst sourceCode = sourceMatch ? sourceMatch[1].toUpperCase() : '';\nconst sourceName = codeToNameMap[sourceCode] || sourceCode;\n\n// Build prompt based on email type\nlet prompt = '';\nif (emailType === 'CategoryA') {\n  prompt = 'Subject: ' + subject + '\\n\\nThis is a CANCELLATION notice. Extract cancellation details.';\n} else if (emailType === 'CategoryB') {\n  prompt = 'Subject: ' + subject + '\\n\\nThis is an UPDATE notice. Extract the changes.';\n} else {\n  prompt = 'Subject: ' + subject + '\\n\\nExtract all relevant information from this image.';\n}\n\nlet userContent = [{ type: 'text', text: prompt }];\nfor (const url of s3Urls) {\n  userContent.push({ type: 'image_url', image_url: { url: url, detail: 'high' } });\n}\n\n// ============================================================\n// CUSTOMIZE: Update the system prompt and fields for your use case\n// ============================================================\nconst systemPrompt = `You are a data extraction expert. Extract structured information accurately from images.\n\n【Fields to extract】\n- date (YYYY-MM-DD format)\n- day_of_week\n- start_time (HH:MM format)\n- end_time (HH:MM format)\n- source_code\n- name\n- amount (numbers only)\n- category\n- type\n- notes\n\n【Output】JSON array only, no explanation`;\n\nconst gptBody = {\n  model: 'gpt-4o',\n  messages: [\n    { role: 'system', content: systemPrompt },\n    { role: 'user', content: userContent }\n  ],\n  max_tokens: 4096,\n  temperature: 0.1\n};\n\nreturn {\n  json: {\n    _meta_subject: subject,\n    _meta_from: item._meta_from,\n    _meta_id: emailId,\n    _meta_attachments: item._meta_attachments || [],\n    _meta_process_type: 'pptx_pdf',\n    _meta_source_name: sourceName,\n    _email_type: emailType,\n    gpt_request_body: JSON.stringify(gptBody)\n  }\n};"},"typeVersion":2},{"id":"eeb04c4a-6278-4f95-b7a1-270ea8052e46","name":"GPT Vision Analysis","type":"n8n-nodes-base.httpRequest","position":[5664,1264],"parameters":{"url":"https://api.openai.com/v1/chat/completions","method":"POST","options":{"timeout":300000},"jsonBody":"={{ $json.gpt_request_body }}","sendBody":true,"specifyBody":"json","authentication":"predefinedCredentialType","nodeCredentialType":"openAiApi"},"typeVersion":4.2},{"id":"59b8220a-6ea1-42d6-88c4-91225668facf","name":"IF - Has Image?","type":"n8n-nodes-base.if","position":[4592,1648],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"operator":{"type":"boolean","operation":"equals"},"leftValue":"={{ $json.has_image }}","rightValue":true}]}},"typeVersion":2},{"id":"68b29f63-ef7c-437a-806b-19ae1e9e1be3","name":"Prepare Image Metadata","type":"n8n-nodes-base.code","position":[4800,1616],"parameters":{"mode":"runOnceForEachItem","jsCode":"// ============================================================\n// PREPARE IMAGE METADATA FOR S3 UPLOAD\n// ============================================================\n\nconst item = $input.item;\nconst json = item.json;\nconst subject = json.Subject || '';\nconst emailId = json.id || 'unknown';\n\nlet emailType = json._email_type || '';\nif (!emailType && emailId) {\n  try {\n    const filteredItems = $('Filter Valid Emails').all();\n    for (const f of filteredItems) {\n      if (f.json.id === emailId && f.json._email_type) {\n        emailType = f.json._email_type;\n        break;\n      }\n    }\n  } catch(e) {}\n}\nemailType = emailType || 'CategoryC';\n\nconst timestamp = Date.now();\nconst fileName = 'email-image-' + emailId + '-' + timestamp + '.png';\n\nreturn {\n  json: {\n    _meta_subject: subject,\n    _meta_from: json.From,\n    _meta_id: json.id,\n    _meta_attachments: json.attachments || [],\n    _meta_process_type: 'image',\n    _email_type: emailType,\n    _image_key: json.image_binary_key || 'attachment_0',\n    _s3_filename: fileName\n  },\n  binary: item.binary\n};"},"typeVersion":2},{"id":"45f24d85-b9d3-4532-997c-55d6036aaa4a","name":"S3 Upload (Image)","type":"n8n-nodes-base.awsS3","position":[5024,1648],"parameters":{"fileName":"={{ $json._s3_filename }}","operation":"upload","bucketName":"={{ $vars.S3_BUCKET_NAME || 'your-bucket-name' }}","additionalFields":{},"binaryPropertyName":"={{ $json._image_key }}"},"typeVersion":2},{"id":"e6c8c547-bd98-4e4e-9c7f-8efb515349f1","name":"Prepare GPT Request (Image)","type":"n8n-nodes-base.code","position":[5248,1648],"parameters":{"mode":"runOnceForEachItem","jsCode":"// ============================================================\n// PREPARE GPT VISION REQUEST FOR IMAGE ATTACHMENTS\n// ============================================================\n\nconst codeToNameMap = {\n  // 'CODE1': 'Full Name 1',\n};\n\nconst item = $input.item.json;\nlet meta = {};\ntry { meta = $('Prepare Image Metadata').first().json; } catch(e) {}\n\nconst subject = meta._meta_subject || '';\nconst emailId = meta._meta_id || '';\nconst emailType = meta._email_type || 'CategoryC';\n\nconst sourceMatch = subject.match(/【([A-Za-z]+)】/i);\nconst sourceCode = sourceMatch ? sourceMatch[1].toUpperCase() : '';\nconst sourceName = codeToNameMap[sourceCode] || sourceCode;\n\nconst imageUrl = item.Location || '';\nif (!imageUrl) return { json: { _skip: true } };\n\nlet prompt = '';\nif (emailType === 'CategoryA') {\n  prompt = 'Subject: ' + subject + '\\n\\nThis is a CANCELLATION notice.';\n} else if (emailType === 'CategoryB') {\n  prompt = 'Subject: ' + subject + '\\n\\nThis is an UPDATE notice.';\n} else {\n  prompt = 'Subject: ' + subject + '\\n\\nExtract all relevant data from this image.';\n}\n\nconst gptBody = {\n  model: 'gpt-4o',\n  messages: [\n    { role: 'system', content: 'You are a data extraction expert. Extract structured information from images.\\n\\n【Fields】date(YYYY-MM-DD), day_of_week, start_time(HH:MM), end_time(HH:MM), source_code, name, amount(numbers only), category, type, notes\\n\\n【Output】JSON array only' },\n    { role: 'user', content: [{ type: 'text', text: prompt }, { type: 'image_url', image_url: { url: imageUrl, detail: 'high' } }] }\n  ],\n  max_tokens: 4096\n};\n\nreturn {\n  json: {\n    _meta_subject: subject,\n    _meta_from: meta._meta_from || '',\n    _meta_id: emailId,\n    _meta_attachments: meta._meta_attachments || [],\n    _meta_process_type: 'image',\n    _meta_source_name: sourceName,\n    _email_type: emailType,\n    gpt_request_body: JSON.stringify(gptBody)\n  }\n};"},"typeVersion":2},{"id":"16937348-70e9-46da-89eb-9ab047956476","name":"GPT Image Analysis","type":"n8n-nodes-base.httpRequest","position":[5472,1760],"parameters":{"url":"https://api.openai.com/v1/chat/completions","method":"POST","options":{},"jsonBody":"={{ $json.gpt_request_body }}","sendBody":true,"specifyBody":"json","authentication":"predefinedCredentialType","nodeCredentialType":"openAiApi"},"typeVersion":4.2},{"id":"3b289245-1499-4dd3-849f-2b39cf04f65a","name":"Prepare GPT Request (Text)","type":"n8n-nodes-base.code","position":[4800,1840],"parameters":{"mode":"runOnceForEachItem","jsCode":"// ============================================================\n// PREPARE GPT REQUEST FOR TEXT-ONLY EMAILS\n// ============================================================\n\nconst item = $input.item.json;\nconst subject = item.Subject || '';\nconst emailId = item.id || '';\n\nlet emailType = item._email_type || '';\nif (!emailType && emailId) {\n  try {\n    const filteredItems = $('Filter Valid Emails').all();\n    for (const f of filteredItems) {\n      if (f.json.id === emailId && f.json._email_type) {\n        emailType = f.json._email_type;\n        break;\n      }\n    }\n  } catch(e) {}\n}\nemailType = emailType || 'CategoryC';\n\nconst textBody = item.textPlain || item.text || '';\nlet userPrompt = 'Subject: ' + subject + '\\n\\nBody:\\n' + textBody.substring(0, 3000);\nif (emailType === 'CategoryA') userPrompt = '【CANCELLATION】' + userPrompt;\nelse if (emailType === 'CategoryB') userPrompt = '【UPDATE】' + userPrompt;\n\nconst gptBody = {\n  model: 'gpt-4o-mini',\n  messages: [\n    { role: 'system', content: 'You are a data extraction expert. Extract structured information from email text.\\n\\n【Fields】date, day_of_week, start_time, end_time, source_code, name, amount, category, type, notes\\n\\n【Output】JSON array only' },\n    { role: 'user', content: userPrompt }\n  ],\n  max_tokens: 2048\n};\n\nreturn {\n  json: {\n    _meta_subject: subject,\n    _meta_from: item.From,\n    _meta_id: emailId,\n    _meta_attachments: item.attachments || [],\n    _meta_process_type: 'text',\n    _email_type: emailType,\n    gpt_request_body: JSON.stringify(gptBody)\n  }\n};"},"typeVersion":2},{"id":"27d9b48b-9762-43e5-b624-f810651664e3","name":"GPT Text Analysis","type":"n8n-nodes-base.httpRequest","position":[5024,1840],"parameters":{"url":"https://api.openai.com/v1/chat/completions","method":"POST","options":{},"jsonBody":"={{ $json.gpt_request_body }}","sendBody":true,"specifyBody":"json","authentication":"predefinedCredentialType","nodeCredentialType":"openAiApi"},"typeVersion":4.2},{"id":"f0ac5f4d-bf3b-4a45-ab48-b1cf17f43ce1","name":"Merge All Paths","type":"n8n-nodes-base.merge","position":[5904,1536],"parameters":{},"typeVersion":3,"alwaysOutputData":true},{"id":"0a1f3790-23fa-4b48-a1c6-857c88758b86","name":"Parse All Results","type":"n8n-nodes-base.code","position":[6128,1536],"parameters":{"jsCode":"// ============================================================\n// PARSE ALL GPT RESPONSES AND FORMAT FOR OUTPUT\n// ============================================================\n// Customize the output schema for your Google Sheets\n// ============================================================\n\n// Map codes to full names (customize for your use case)\nconst codeToNameMap = {\n  // 'CODE1': 'Full Name 1',\n  // 'CODE2': 'Full Name 2',\n};\n\nconst allItems = $input.all();\nconst results = [];\n\nlet pptxPrepItems = [], imagePrepItems = [], textPrepItems = [];\ntry { pptxPrepItems = $('Prepare GPT Request (PPTX)').all(); } catch(e) {}\ntry { imagePrepItems = $('Prepare GPT Request (Image)').all(); } catch(e) {}\ntry { textPrepItems = $('Prepare GPT Request (Text)').all(); } catch(e) {}\n\nlet pptxPrepIndex = 0, imagePrepIndex = 0, textPrepIndex = 0;\n\nfor (let i = 0; i < allItems.length; i++) {\n  const item = allItems[i];\n  const data = item.json;\n  \n  const hasMetadata = !!data._meta_id;\n  let subject, from, emailId, attachments, sourceName, emailType;\n  \n  if (hasMetadata) {\n    subject = data._meta_subject || '';\n    from = data._meta_from || '';\n    emailId = data._meta_id || '';\n    attachments = data._meta_attachments || [];\n    sourceName = data._meta_source_name || '';\n    emailType = data._email_type || 'CategoryC';\n  } else {\n    const modelName = data.model || '';\n    const isTextModel = modelName.includes('gpt-4o-mini');\n    let meta = null;\n    if (isTextModel && textPrepIndex < textPrepItems.length) {\n      meta = textPrepItems[textPrepIndex].json;\n      textPrepIndex++;\n    } else if (imagePrepIndex < imagePrepItems.length) {\n      meta = imagePrepItems[imagePrepIndex].json;\n      imagePrepIndex++;\n    } else if (pptxPrepIndex < pptxPrepItems.length) {\n      meta = pptxPrepItems[pptxPrepIndex].json;\n      pptxPrepIndex++;\n    }\n\n    if (meta) {\n      subject = meta._meta_subject || '';\n      from = meta._meta_from || '';\n      emailId = meta._meta_id || '';\n      attachments = meta._meta_attachments || [];\n      sourceName = meta._meta_source_name || '';\n      emailType = meta._email_type || 'CategoryC';\n    } else {\n      subject = ''; from = ''; emailId = ''; attachments = []; sourceName = ''; emailType = 'CategoryC';\n    }\n  }\n  \n  if (from && typeof from === 'object') {\n    from = from.text || from.address || (from.value && from.value[0] ? from.value[0].address : '') || '';\n  }\n  from = String(from || '');\n  \n  let content = '';\n  if (data.choices && data.choices[0] && data.choices[0].message) {\n    content = data.choices[0].message.content || '';\n  }\n  \n  // Check for unreadable content\n  const unreadablePatterns = ['sorry', 'cannot read', 'unable to', 'not able to'];\n  const isUnreadable = unreadablePatterns.some(p => content.toLowerCase().includes(p));\n  \n  if (isUnreadable) {\n    results.push({\n      json: {\n        email_type: emailType,\n        date: '', day_of_week: '', start_time: '', end_time: '',\n        source_name: sourceName, name: '', amount: '0', category: '', type: '',\n        status: 'Needs Review', client: '',\n        notes: '⚠️File unreadable - manual review required',\n        created_at: new Date().toISOString(),\n        email_subject: subject, sender: from, email_id: emailId,\n        attachments: attachments.map(a => a.filename || '').join(', '),\n        _unreadable: true\n      }\n    });\n    continue;\n  }\n  \n  // Parse JSON from GPT response\n  let items = [];\n  try {\n    let jsonStr = content;\n    const codeMatch = content.match(/```json\\s*([\\s\\S]*?)\\s*```/);\n    if (codeMatch) jsonStr = codeMatch[1];\n    else {\n      const arrMatch = content.match(/\\[[\\s\\S]*\\]/);\n      if (arrMatch) jsonStr = arrMatch[0];\n    }\n    const parsed = JSON.parse(jsonStr);\n    items = Array.isArray(parsed) ? parsed : [parsed];\n  } catch(e) { \n    if (emailId) {\n      results.push({\n        json: {\n          email_type: emailType,\n          date: '', day_of_week: '', start_time: '', end_time: '',\n          source_name: sourceName, name: '', amount: '0', category: '', type: '',\n          status: 'Needs Review', client: '',\n          notes: '⚠️Parse failed - manual review required',\n          created_at: new Date().toISOString(),\n          email_subject: subject, sender: from, email_id: emailId,\n          attachments: attachments.map(a => a.filename || '').join(', '),\n          _unreadable: true\n        }\n      });\n    }\n    continue;\n  }\n  \n  if (items.length === 0) continue;\n  \n  // ============================================================\n  // CUSTOMIZE: Map extracted data to your output schema\n  // ============================================================\n  for (const s of items) {\n    let sourceNameFinal = sourceName || s.source_name || '';\n    if (s.source_code) {\n      const code = s.source_code.toUpperCase();\n      sourceNameFinal = codeToNameMap[code] || sourceNameFinal || code;\n    }\n    \n    // Determine category from time (customize as needed)\n    let category = s.category || '';\n    if (!category && s.start_time) {\n      const h = parseInt(s.start_time.split(':')[0]) || 0;\n      if (h >= 5 && h < 10) category = 'Morning';\n      else if (h >= 10 && h < 15) category = 'Afternoon';\n      else if (h >= 15 && h < 23) category = 'Evening';\n      else category = 'Night';\n    }\n\n    results.push({\n      json: {\n        email_type: emailType,\n        date: s.date || '',\n        day_of_week: s.day_of_week || '',\n        start_time: s.start_time || '',\n        end_time: s.end_time || '',\n        source_name: sourceNameFinal,\n        name: s.name || '',\n        amount: String(s.amount || 0),\n        category: category,\n        type: s.type || 'Regular',\n        status: 'Pending',\n        client: '',\n        notes: s.notes || '',\n        created_at: new Date().toISOString(),\n        email_subject: subject,\n        sender: from,\n        email_id: emailId,\n        attachments: attachments.map(a => a.filename || '').join(', ')\n      }\n    });\n  }\n}\n\nif (results.length === 0) return [{ json: { _no_data: true } }];\nreturn results;"},"typeVersion":2},{"id":"3b91edb4-d360-4441-ac58-bcaf1da85514","name":"Filter Valid Data","type":"n8n-nodes-base.filter","position":[6352,1536],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"operator":{"type":"string","operation":"notEmpty","singleValue":true},"leftValue":"={{ $json[\"email_type\"] }}","rightValue":true}]}},"typeVersion":2},{"id":"07942913-2997-42f5-a93f-b4459ca4c119","name":"Save to Google Sheets","type":"n8n-nodes-base.googleSheets","position":[6560,1536],"parameters":{"columns":{"value":{},"mappingMode":"autoMapInputData"},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"name","value":"Sheet1"},"documentId":{"__rl":true,"mode":"id","value":"={{ $vars.GOOGLE_SHEET_ID || 'your-google-sheet-id' }}"}},"typeVersion":4.4},{"id":"93650748-aff3-423d-afa6-48dd53b5230b","name":"Extract Email IDs","type":"n8n-nodes-base.code","position":[6784,1536],"parameters":{"jsCode":"// ============================================================\n// EXTRACT UNIQUE EMAIL IDs FOR LABELING\n// ============================================================\n\nconst allItems = $input.all();\nconst emailMap = new Map();\n\nfunction addEmail(emailId, emailType, isUnreadable) {\n  if (!emailId) return;\n  if (!emailMap.has(emailId)) {\n    emailMap.set(emailId, { type: isUnreadable ? 'NeedsReview' : emailType });\n  }\n  if (isUnreadable) emailMap.get(emailId).type = 'NeedsReview';\n}\n\nfor (const item of allItems) {\n  const emailId = item.json.email_id;\n  const emailType = item.json.email_type || 'CategoryC';\n  const isUnreadable = item.json._unreadable === true;\n  addEmail(emailId, emailType, isUnreadable);\n}\n\ntry {\n  const filteredItems = $('Filter Valid Emails').all();\n  for (const f of filteredItems) {\n    addEmail(f.json.id, f.json._email_type || 'CategoryC', false);\n  }\n} catch (e) {}\n\nreturn Array.from(emailMap.entries()).map(([id, data]) => ({\n  json: { email_id: id, email_type: data.type }\n}));"},"typeVersion":2},{"id":"613a3c8a-e875-48f5-b75b-1b121b92f173","name":"Get Gmail Labels","type":"n8n-nodes-base.httpRequest","position":[6784,1760],"parameters":{"url":"https://gmail.googleapis.com/gmail/v1/users/me/labels","options":{},"authentication":"predefinedCredentialType","nodeCredentialType":"gmailOAuth2"},"typeVersion":4.2},{"id":"dab2e312-225d-4df4-a45d-4b1ab1a37558","name":"Check Labels","type":"n8n-nodes-base.code","position":[7008,1760],"parameters":{"jsCode":"// ============================================================\n// CHECK WHICH LABELS EXIST\n// ============================================================\n// Customize label names for your use case\n// ============================================================\n\nconst response = $input.first().json;\nconst labels = response.labels || [];\n\n// Define your label names here\nconst labelNames = [\n  'Processed_CategoryA',\n  'Processed_CategoryB', \n  'Processed_CategoryC'\n];\n\nconst existingLabels = {};\nconst missingLabels = [];\n\nfor (const name of labelNames) {\n  const found = labels.find(l => l.name === name);\n  if (found) existingLabels[name] = found.id;\n  else missingLabels.push(name);\n}\n\nreturn [{ \n  json: { \n    existing_labels: existingLabels, \n    missing_labels: missingLabels, \n    needs_creation: missingLabels.length > 0 \n  } \n}];"},"typeVersion":2},{"id":"f169d45b-e952-4df5-bd93-7e61a4f07980","name":"IF - Need Labels?","type":"n8n-nodes-base.if","position":[7232,1760],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"operator":{"type":"boolean","operation":"equals"},"leftValue":"={{ $json.needs_creation }}","rightValue":true}]}},"typeVersion":2},{"id":"137c484a-e4ce-4996-b603-4f2164309dc3","name":"Prepare Label Creation","type":"n8n-nodes-base.code","position":[7440,1664],"parameters":{"jsCode":"// Prepare label creation requests\nconst items = $input.all();\nconst results = [];\nfor (const item of items) {\n  const data = item.json || {};\n  const missingLabels = data.missing_labels || [];\n  const existingLabels = data.existing_labels || {};\n  if (missingLabels.length === 0) results.push({ json: { label_name: '', existing_labels: existingLabels, _skip_creation: true } });\n  else for (const name of missingLabels) results.push({ json: { label_name: name, existing_labels: existingLabels, _skip_creation: false } });\n}\nif (results.length === 0) return [{ json: { label_name: '', existing_labels: {}, _skip_creation: true } }];\nreturn results;"},"typeVersion":2},{"id":"28717cfd-d0dc-4040-b3fa-dd2982e05819","name":"Filter Labels to Create","type":"n8n-nodes-base.filter","position":[7664,1664],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"operator":{"type":"boolean","operation":"notEquals"},"leftValue":"={{ $json._skip_creation }}","rightValue":true}]}},"typeVersion":2},{"id":"5f7f79dc-0cb8-43b8-b938-6539881c02d8","name":"Create Gmail Label","type":"n8n-nodes-base.httpRequest","position":[7888,1664],"parameters":{"url":"https://gmail.googleapis.com/gmail/v1/users/me/labels","method":"POST","options":{},"jsonBody":"={\"name\": \"{{ $json.label_name }}\", \"labelListVisibility\": \"labelShow\", \"messageListVisibility\": \"show\"}","sendBody":true,"specifyBody":"json","authentication":"predefinedCredentialType","nodeCredentialType":"gmailOAuth2"},"typeVersion":4.2},{"id":"13873411-e770-4f62-a957-f0dd3690b26d","name":"Get New Label IDs","type":"n8n-nodes-base.code","position":[8112,1664],"parameters":{"jsCode":"// Collect newly created label IDs\nconst allItems = $input.all();\nconst createdLabels = {};\nfor (const item of allItems) {\n  const data = item.json;\n  if (data.id && data.name) createdLabels[data.name] = data.id;\n}\nlet existingLabels = {};\ntry { const labelCheckData = $('Check Labels').first().json; existingLabels = labelCheckData.existing_labels || {}; } catch(e) {}\nreturn [{ json: { all_labels: { ...existingLabels, ...createdLabels } } }];"},"typeVersion":2},{"id":"8ca950ac-7f74-4596-adf7-ca74e6b191d3","name":"Pass Existing Labels","type":"n8n-nodes-base.code","position":[7440,1856],"parameters":{"jsCode":"// Pass through existing labels when no creation needed\nconst items = $input.all();\nlet existingLabels = {};\nfor (const item of items) {\n  const data = item.json || {};\n  if (data.existing_labels) existingLabels = { ...existingLabels, ...data.existing_labels };\n}\nreturn [{ json: { all_labels: existingLabels } }];"},"typeVersion":2},{"id":"bebcee89-dc11-4bab-8a9c-e72744778b07","name":"Merge Label IDs","type":"n8n-nodes-base.code","position":[8112,1856],"parameters":{"jsCode":"// Merge all label IDs\nconst items = $input.all();\nlet allLabels = {};\nfor (const item of items) {\n  const data = item.json || {};\n  const labels = data.all_labels || data.existing_labels || {};\n  allLabels = { ...allLabels, ...labels };\n}\nreturn [{ json: { all_labels: allLabels } }];"},"typeVersion":2},{"id":"ec6d8a00-ade2-4047-84fa-2f448cceb979","name":"Prepare Label Application","type":"n8n-nodes-base.code","position":[8320,1776],"parameters":{"jsCode":"// ============================================================\n// PREPARE LABEL APPLICATION FOR EACH EMAIL\n// ============================================================\n// Customize the type-to-label mapping\n// ============================================================\n\nconst inputItems = $input.all();\nconst labelInfo = inputItems[0]?.json?.all_labels\n  ? inputItems[0].json\n  : (() => {\n      try { return $('Merge Label IDs').first().json; } catch (e) { return {}; }\n    })();\n\nlet emailItems = [];\ntry { emailItems = $('Extract Email IDs').all(); } catch (e) { emailItems = []; }\nif (emailItems.length === 0) {\n  try {\n    const filteredItems = $('Filter Valid Emails').all();\n    emailItems = filteredItems\n      .map(item => ({\n        json: {\n          email_id: item.json.id || '',\n          email_type: item.json._email_type || 'CategoryC'\n        }\n      }))\n      .filter(item => item.json.email_id);\n  } catch (e) {\n    emailItems = [];\n  }\n}\n\nconst allLabels = labelInfo.all_labels || {};\n\n// Map email types to label names (customize these)\nconst typeToLabel = { \n  'CategoryA': 'Processed_CategoryA', \n  'CategoryB': 'Processed_CategoryB', \n  'CategoryC': 'Processed_CategoryC' \n};\n\nconst results = [];\n\nfor (const item of emailItems) {\n  const emailType = item.json.email_type || 'CategoryC';\n  const labelName = typeToLabel[emailType] || 'Processed_CategoryC';\n  const labelId = allLabels[labelName];\n  if (labelId) {\n    results.push({\n      json: {\n        email_id: item.json.email_id,\n        email_type: emailType,\n        label_name: labelName,\n        label_id: labelId,\n        request_body: JSON.stringify({ addLabelIds: [labelId] })\n      }\n    });\n  }\n}\n\nif (results.length === 0) return [{ json: { _no_emails: true } }];\nreturn results;"},"typeVersion":2},{"id":"b6a78bf7-4974-47ee-8500-440c30991c39","name":"Has Emails?","type":"n8n-nodes-base.filter","position":[8544,1776],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"operator":{"type":"boolean","operation":"notEquals"},"leftValue":"={{ $json._no_emails }}","rightValue":true}]}},"typeVersion":2},{"id":"f1e17c5b-24d1-4168-9b06-f24be083c53f","name":"Apply Gmail Labels","type":"n8n-nodes-base.httpRequest","position":[8768,1776],"parameters":{"url":"=https://gmail.googleapis.com/gmail/v1/users/me/messages/{{ $json.email_id }}/modify","method":"POST","options":{},"jsonBody":"={{ $json.request_body }}","sendBody":true,"specifyBody":"json","authentication":"predefinedCredentialType","nodeCredentialType":"gmailOAuth2"},"typeVersion":4.2},{"id":"config-variables-note","name":"Configuration Variables","type":"n8n-nodes-base.stickyNote","position":[2384,2040],"parameters":{"color":4,"width":500,"height":300,"content":"###  Required Variables\n\nSet these in n8n Settings > Variables:\n\n| Variable | Description |\n|----------|-------------|\n| `S3_BUCKET_NAME` | Your AWS S3 bucket name |\n| `AWS_REGION` | AWS region (e.g., us-east-1) |\n| `GOOGLE_SHEET_ID` | Google Sheets document ID |\n\n### Required Credentials\n\n- **Gmail OAuth2** - For email access\n- **OpenAI API** - For GPT Vision\n- **AWS S3** - For image storage\n- **Google Sheets OAuth2** - For data output\n- **ConvertAPI HTTP Header Auth** - For file conversion"},"typeVersion":1}],"pinData":{},"connections":{"Has Emails?":{"main":[[{"node":"Apply Gmail Labels","type":"main","index":0}]]},"Check Labels":{"main":[[{"node":"IF - Need Labels?","type":"main","index":0}]]},"Domain Filter":{"main":[[{"node":"Filter Valid Emails","type":"main","index":0}]]},"Collect S3 URLs":{"main":[[{"node":"Prepare GPT Request (PPTX)","type":"main","index":0}]]},"IF - Has Excel?":{"main":[[{"node":"Extract from Excel","type":"main","index":0}],[{"node":"IF - Has PPTX/PDF?","type":"main","index":0}]]},"IF - Has Image?":{"main":[[{"node":"Prepare Image Metadata","type":"main","index":0}],[{"node":"Prepare GPT Request (Text)","type":"main","index":0}]]},"Merge All Paths":{"main":[[{"node":"Parse All Results","type":"main","index":0}]]},"Merge Label IDs":{"main":[[{"node":"Prepare Label Application","type":"main","index":0}]]},"Get Gmail Labels":{"main":[[{"node":"Check Labels","type":"main","index":0}]]},"Parse Excel Data":{"main":[[{"node":"Merge All Paths","type":"main","index":0}]]},"S3 Upload (PPTX)":{"main":[[{"node":"Collect S3 URLs","type":"main","index":0}]]},"Schedule Trigger":{"main":[[{"node":"Gmail - Get Emails","type":"main","index":0}]]},"ConvertAPI to PNG":{"main":[[{"node":"Prepare for S3 Upload","type":"main","index":0}]]},"Detect File Types":{"main":[[{"node":"IF - Has Excel?","type":"main","index":0}]]},"Filter Valid Data":{"main":[[{"node":"Save to Google Sheets","type":"main","index":0},{"node":"Extract Email IDs","type":"main","index":0}]]},"GPT Text Analysis":{"main":[[{"node":"Merge All Paths","type":"main","index":0}]]},"Get New Label IDs":{"main":[[{"node":"Merge Label IDs","type":"main","index":0}]]},"IF - Need Labels?":{"main":[[{"node":"Prepare Label Creation","type":"main","index":0}],[{"node":"Pass Existing Labels","type":"main","index":0}]]},"Parse All Results":{"main":[[{"node":"Filter Valid Data","type":"main","index":0}]]},"S3 Upload (Image)":{"main":[[{"node":"Prepare GPT Request (Image)","type":"main","index":0}]]},"Create Gmail Label":{"main":[[{"node":"Get New Label IDs","type":"main","index":0}]]},"Extract from Excel":{"main":[[{"node":"Parse Excel Data","type":"main","index":0}]]},"GPT Image Analysis":{"main":[[{"node":"Merge All Paths","type":"main","index":0}]]},"Gmail - Get Emails":{"main":[[{"node":"Domain Filter","type":"main","index":0}]]},"IF - Has PPTX/PDF?":{"main":[[{"node":"ConvertAPI to PNG","type":"main","index":0}],[{"node":"IF - Has Image?","type":"main","index":0}]]},"Filter Valid Emails":{"main":[[{"node":"Gmail - Get Attachments","type":"main","index":0}]]},"GPT Vision Analysis":{"main":[[{"node":"Merge All Paths","type":"main","index":0}]]},"Pass Existing Labels":{"main":[[{"node":"Merge Label IDs","type":"main","index":0}]]},"Prepare for S3 Upload":{"main":[[{"node":"S3 Upload (PPTX)","type":"main","index":0}]]},"Save to Google Sheets":{"main":[[{"node":"Get Gmail Labels","type":"main","index":0}]]},"Prepare Image Metadata":{"main":[[{"node":"S3 Upload (Image)","type":"main","index":0}]]},"Prepare Label Creation":{"main":[[{"node":"Filter Labels to Create","type":"main","index":0}]]},"Filter Labels to Create":{"main":[[{"node":"Create Gmail Label","type":"main","index":0}]]},"Gmail - Get Attachments":{"main":[[{"node":"Detect File Types","type":"main","index":0}]]},"Prepare Label Application":{"main":[[{"node":"Has Emails?","type":"main","index":0}]]},"Prepare GPT Request (PPTX)":{"main":[[{"node":"GPT Vision Analysis","type":"main","index":0}]]},"Prepare GPT Request (Text)":{"main":[[{"node":"GPT Text Analysis","type":"main","index":0}]]},"Prepare GPT Request (Image)":{"main":[[{"node":"GPT Image Analysis","type":"main","index":0}]]}},"description":"Automatically extract structured information from emails using AI-powered document analysis. This workflow processes emails from specified domains, classifies them by type, and extracts structured data from various attachment formats.\n\n## Who is this for\nOperations teams, coordinators, and business professionals who receive proposals or reports from multiple sources via email and need to consolidate the information into a structured database.\n\n## What this workflow does\n1. **Monitors Gmail** every 30 minutes for emails from specified domains\n2. **Classifies emails** into three categories based on customizable keywords\n3. **Processes attachments** intelligently based on file type and email classification\n4. **Extracts structured data**: dates, times, names, amounts, and other fields\n5. **Saves to Google Sheets** with full metadata and classification\n6. **Labels processed emails** in Gmail for tracking\n\n## Setup requirements\n- Gmail OAuth2 credentials\n- OpenAI API key (GPT-4 Vision)\n- Google Sheets OAuth2 credentials\n- AWS S3 bucket for temporary image storage\n- ConvertAPI account for PPTX/PDF conversion\n\n## How to customize\nEdit the domain list and classification keywords in the code nodes to adapt for your specific use case."},"lastUpdatedBy":29,"workflowInfo":{"nodeCount":48,"nodeTypes":{"n8n-nodes-base.if":{"count":4},"n8n-nodes-base.code":{"count":17},"n8n-nodes-base.awsS3":{"count":2},"n8n-nodes-base.gmail":{"count":2},"n8n-nodes-base.merge":{"count":1},"n8n-nodes-base.filter":{"count":4},"n8n-nodes-base.stickyNote":{"count":8},"n8n-nodes-base.httpRequest":{"count":7},"n8n-nodes-base.googleSheets":{"count":1},"n8n-nodes-base.extractFromFile":{"count":1},"n8n-nodes-base.scheduleTrigger":{"count":1}}},"status":"published","readyToDemo":null,"user":{"name":"Masaki Go","username":"pippi","bio":"I’m the CEO of HumanoiD Inc., a company that provides business process optimization and AI implementation services to organizations of all kinds.","verified":true,"links":["https://humanoid-inc.net/"],"avatar":"https://gravatar.com/avatar/3e5a84235f796f5fe58271af90d091573dbef7c0999a83800d7b1b6d94c0e16a?r=pg&d=retro&size=200"},"nodes":[{"id":18,"icon":"file:googleSheets.svg","name":"n8n-nodes-base.googleSheets","codex":{"data":{"alias":["CSV","Sheet","Spreadsheet","GS"],"resources":{"generic":[{"url":"https://n8n.io/blog/love-at-first-sight-ricardos-n8n-journey/","icon":"❤️","label":"Love at first sight: Ricardo’s n8n journey"},{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/","icon":"🧾","label":"Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"},{"url":"https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/","icon":"🎫","label":"Supercharging your conference registration process with n8n"},{"url":"https://n8n.io/blog/creating-triggers-for-n8n-workflows-using-polling/","icon":"⏲","label":"Creating triggers for n8n workflows using polling"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/migrating-community-metrics-to-orbit-using-n8n/","icon":"📈","label":"Migrating Community Metrics to Orbit using n8n"},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/your-business-doesnt-need-you-to-operate/","icon":" 🖥️","label":"Hey founders! Your business doesn't need you to operate"},{"url":"https://n8n.io/blog/how-honest-burgers-use-automation-to-save-100k-per-year/","icon":"🍔","label":"How Honest Burgers Use Automation to Save $100k per year"},{"url":"https://n8n.io/blog/how-a-digital-strategist-uses-n8n-for-online-marketing/","icon":"💻","label":"How a digital strategist uses n8n for online marketing"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/","icon":"📈","label":"How a Membership Development Manager automates his work and investments"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/google/oauth-single-service/"}]},"categories":["Data & Storage","Productivity"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"input\",\"output\"]","defaults":{"name":"Google Sheets"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MCIgaGVpZ2h0PSI2MCI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGZpbGw9IiMyOEI0NDYiIGQ9Ik0zNS42OSAxIDUyIDE3LjIyNXYzOS4wODdhMy42NyAzLjY3IDAgMCAxLTEuMDg0IDIuNjFBMy43IDMuNyAwIDAgMSA0OC4yOTMgNjBIMTIuNzA3YTMuNyAzLjcgMCAwIDEtMi42MjMtMS4wNzhBMy42NyAzLjY3IDAgMCAxIDkgNTYuMzEyVjQuNjg4YTMuNjcgMy42NyAwIDAgMSAxLjA4NC0yLjYxQTMuNyAzLjcgMCAwIDEgMTIuNzA3IDF6Ii8+PHBhdGggZmlsbD0iIzZBQ0U3QyIgZD0iTTM1LjY5IDEgNTIgMTcuMjI1SDM5LjM5N2MtMi4wNTQgMC0zLjcwNy0xLjgyOS0zLjcwNy0zLjg3MnoiLz48cGF0aCBmaWxsPSIjMjE5QjM4IiBkPSJNMzkuMjExIDE3LjIyNSA1MiAyMi40OHYtNS4yNTV6Ii8+PHBhdGggZmlsbD0iI0ZGRiIgZD0iTTIwLjEyIDMxLjk3NWMwLS44MTcuNjYyLTEuNDc1IDEuNDgzLTEuNDc1aDE3Ljc5NGMuODIxIDAgMS40ODIuNjU4IDEuNDgyIDEuNDc1djE1LjQ4N2MwIC44MTgtLjY2MSAxLjQ3NS0xLjQ4MiAxLjQ3NUgyMS42MDNhMS40NzYgMS40NzYgMCAwIDEtMS40ODItMS40NzRWMzEuOTc0em0yLjIyNSAxLjQ3NWg2LjY3MnYyLjIxMmgtNi42NzJ6bTAgNS4xNjJoNi42NzJ2Mi4yMTNoLTYuNjcyem0wIDUuMTYzaDYuNjcydjIuMjEyaC02LjY3MnptOS42MzgtMTAuMzI1aDYuNjcydjIuMjEyaC02LjY3MnptMCA1LjE2Mmg2LjY3MnYyLjIxM2gtNi42NzJ6bTAgNS4xNjNoNi42NzJ2Mi4yMTJoLTYuNjcyeiIvPjxwYXRoIGZpbGw9IiMyOEI0NDYiIGQ9Ik0zNC42OSAwIDUxIDE2LjIyNXYzOS4wODdhMy42NyAzLjY3IDAgMCAxLTEuMDg0IDIuNjFBMy43IDMuNyAwIDAgMSA0Ny4yOTMgNTlIMTEuNzA3YTMuNyAzLjcgMCAwIDEtMi42MjMtMS4wNzhBMy42NyAzLjY3IDAgMCAxIDggNTUuMzEyVjMuNjg4YTMuNjcgMy42NyAwIDAgMSAxLjA4NC0yLjYxQTMuNyAzLjcgMCAwIDEgMTEuNzA3IDB6Ii8+PHBhdGggZmlsbD0iIzZBQ0U3QyIgZD0iTTM0LjY5IDAgNTEgMTYuMjI1SDM4LjM5N2MtMi4wNTQgMC0zLjcwNy0xLjgyOS0zLjcwNy0zLjg3MnoiLz48cGF0aCBmaWxsPSIjMjE5QjM4IiBkPSJNMzguMjExIDE2LjIyNSA1MSAyMS40OHYtNS4yNTV6Ii8+PHBhdGggZmlsbD0iI0ZGRiIgZD0iTTE5LjEyIDMwLjk3NWMwLS44MTcuNjYyLTEuNDc1IDEuNDgzLTEuNDc1aDE3Ljc5NGMuODIxIDAgMS40ODIuNjU4IDEuNDgyIDEuNDc1djE1LjQ4N2MwIC44MTgtLjY2MSAxLjQ3NS0xLjQ4MiAxLjQ3NUgyMC42MDNhMS40NzYgMS40NzYgMCAwIDEtMS40ODItMS40NzRWMzAuOTc0em0yLjIyNSAxLjQ3NWg2LjY3MnYyLjIxMmgtNi42NzJ6bTAgNS4xNjJoNi42NzJ2Mi4yMTNoLTYuNjcyem0wIDUuMTYzaDYuNjcydjIuMjEyaC02LjY3MnptOS42MzgtMTAuMzI1aDYuNjcydjIuMjEyaC02LjY3MnptMCA1LjE2Mmg2LjY3MnYyLjIxM2gtNi42NzJ6bTAgNS4xNjNoNi42NzJ2Mi4yMTJoLTYuNjcyeiIvPjwvZz48L3N2Zz4="},"displayName":"Google Sheets","typeVersion":5,"nodeCategories":[{"id":3,"name":"Data & Storage"},{"id":4,"name":"Productivity"}]},{"id":19,"icon":"file:httprequest.svg","name":"n8n-nodes-base.httpRequest","codex":{"data":{"alias":["API","Request","URL","Build","cURL"],"resources":{"generic":[{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/automatically-pulling-and-visualizing-data-with-n8n/","icon":"📈","label":"Automatically pulling and visualizing data with n8n"},{"url":"https://n8n.io/blog/learn-how-to-automatically-cross-post-your-content-with-n8n/","icon":"✍️","label":"Learn how to automatically cross-post your content with n8n"},{"url":"https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/","icon":"🧾","label":"Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"},{"url":"https://n8n.io/blog/running-n8n-on-ships-an-interview-with-maranics/","icon":"🛳","label":"Running n8n on ships: An interview with Maranics"},{"url":"https://n8n.io/blog/what-are-apis-how-to-use-them-with-no-code/","icon":" 🪢","label":"What are APIs and how to use them with no code"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/world-poetry-day-workflow/","icon":"📜","label":"Celebrating World Poetry Day with a daily poem in Telegram"},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/automate-designs-with-bannerbear-and-n8n/","icon":"🎨","label":"Automate Designs with Bannerbear and n8n"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/building-an-expense-tracking-app-in-10-minutes/","icon":"📱","label":"Building an expense tracking app in 10 minutes"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/how-to-use-the-http-request-node-the-swiss-army-knife-for-workflow-automation/","icon":"🧰","label":"How to use the HTTP Request Node - The Swiss Army Knife for Workflow Automation"},{"url":"https://n8n.io/blog/learn-how-to-use-webhooks-with-mattermost-slash-commands/","icon":"🦄","label":"Learn how to use webhooks with Mattermost slash commands"},{"url":"https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/","icon":"📈","label":"How a Membership Development Manager automates his work and investments"},{"url":"https://n8n.io/blog/a-low-code-bitcoin-ticker-built-with-questdb-and-n8n-io/","icon":"📈","label":"A low-code bitcoin ticker built with QuestDB and n8n.io"},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/automations-for-activists/","icon":"✨","label":"How Common Knowledge use workflow automation for activism"},{"url":"https://n8n.io/blog/creating-scheduled-text-affirmations-with-n8n/","icon":"🤟","label":"Creating scheduled text affirmations with n8n"},{"url":"https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/","icon":"🛵","label":"How Goomer automated their operations with over 200 n8n workflows"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"output\"]","defaults":{"name":"HTTP Request","color":"#0004F5"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MCAyMEM0MCA4Ljk1MzE0IDMxLjA0NjkgMCAyMCAwQzguOTUzMTQgMCAwIDguOTUzMTQgMCAyMEMwIDMxLjA0NjkgOC45NTMxNCA0MCAyMCA0MEMzMS4wNDY5IDQwIDQwIDMxLjA0NjkgNDAgMjBaTTIwIDM2Ljk0NThDMTguODg1MiAzNi45NDU4IDE3LjEzNzggMzUuOTY3IDE1LjQ5OTggMzIuNjk4NUMxNC43OTY0IDMxLjI5MTggMTQuMTk2MSAyOS41NDMxIDEzLjc1MjYgMjcuNjg0N0gyNi4xODk4QzI1LjgwNDUgMjkuNTQwMyAyNS4yMDQ0IDMxLjI5MDEgMjQuNTAwMiAzMi42OTg1QzIyLjg2MjIgMzUuOTY3IDIxLjExNDggMzYuOTQ1OCAyMCAzNi45NDU4Wk0xMi45MDY0IDIwQzEyLjkwNjQgMjEuNjA5NyAxMy4wMDg3IDIzLjE2NCAxMy4yMDAzIDI0LjYzMDVIMjYuNzk5N0MyNi45OTEzIDIzLjE2NCAyNy4wOTM2IDIxLjYwOTcgMjcuMDkzNiAyMEMyNy4wOTM2IDE4LjM5MDMgMjYuOTkxMyAxNi44MzYgMjYuNzk5NyAxNS4zNjk1SDEzLjIwMDNDMTMuMDA4NyAxNi44MzYgMTIuOTA2NCAxOC4zOTAzIDEyLjkwNjQgMjBaTTIwIDMuMDU0MTlDMjEuMTE0OSAzLjA1NDE5IDIyLjg2MjIgNC4wMzA3OCAyNC41MDAxIDcuMzAwMzlDMjUuMjA2NiA4LjcxNDA4IDI1LjgwNzIgMTAuNDA2NyAyNi4xOTIgMTIuMzE1M0gxMy43NTAxQzE0LjE5MzMgMTAuNDA0NyAxNC43OTQyIDguNzEyNTQgMTUuNDk5OCA3LjMwMDY0QzE3LjEzNzcgNC4wMzA4MyAxOC44ODUxIDMuMDU0MTkgMjAgMy4wNTQxOVpNMzAuMTQ3OCAyMEMzMC4xNDc4IDE4LjQwOTkgMzAuMDU0MyAxNi44NjE3IDI5LjgyMjcgMTUuMzY5NUgzNi4zMDQyQzM2LjcyNTIgMTYuODQyIDM2Ljk0NTggMTguMzk2NCAzNi45NDU4IDIwQzM2Ljk0NTggMjEuNjAzNiAzNi43MjUyIDIzLjE1OCAzNi4zMDQyIDI0LjYzMDVIMjkuODIyN0MzMC4wNTQzIDIzLjEzODMgMzAuMTQ3OCAyMS41OTAxIDMwLjE0NzggMjBaTTI2LjI3NjcgNC4yNTUxMkMyNy42MzY1IDYuMzYwMTkgMjguNzExIDkuMTMyIDI5LjM3NzQgMTIuMzE1M0gzNS4xMDQ2QzMzLjI1MTEgOC42NjggMzAuMTA3IDUuNzgzNDYgMjYuMjc2NyA0LjI1NTEyWk0xMC42MjI2IDEyLjMxNTNINC44OTI5M0M2Ljc1MTQ3IDguNjY3ODQgOS44OTM1MSA1Ljc4MzQxIDEzLjcyMzIgNC4yNTUxM0MxMi4zNjM1IDYuMzYwMjEgMTEuMjg5IDkuMTMyMDEgMTAuNjIyNiAxMi4zMTUzWk0zLjA1NDE5IDIwQzMuMDU0MTkgMjEuNjAzIDMuMjc3NDMgMjMuMTU3NSAzLjY5NDg0IDI0LjYzMDVIMTAuMTIxN0M5Ljk0NjE5IDIzLjE0MiA5Ljg1MjIyIDIxLjU5NDMgOS44NTIyMiAyMEM5Ljg1MjIyIDE4LjQwNTcgOS45NDYxOSAxNi44NTggMTAuMTIxNyAxNS4zNjk1SDMuNjk0ODRDMy4yNzc0MyAxNi44NDI1IDMuMDU0MTkgMTguMzk3IDMuMDU0MTkgMjBaTTI2LjI3NjYgMzUuNzQyN0MyNy42MzY1IDMzLjYzOTMgMjguNzExIDMwLjg2OCAyOS4zNzc0IDI3LjY4NDdIMzUuMTA0NkMzMy4yNTEgMzEuMzMyMiAzMC4xMDY4IDM0LjIxNzkgMjYuMjc2NiAzNS43NDI3Wk0xMy43MjM0IDM1Ljc0MjdDOS44OTM2OSAzNC4yMTc5IDYuNzUxNTUgMzEuMzMyNCA0Ljg5MjkzIDI3LjY4NDdIMTAuNjIyNkMxMS4yODkgMzAuODY4IDEyLjM2MzUgMzMuNjM5MyAxMy43MjM0IDM1Ljc0MjdaIiBmaWxsPSIjM0E0MkU5Ii8+Cjwvc3ZnPgo="},"displayName":"HTTP Request","typeVersion":4,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":20,"icon":"fa:map-signs","name":"n8n-nodes-base.if","codex":{"data":{"alias":["Router","Filter","Condition","Logic","Boolean","Branch"],"details":"The IF node can be used to implement binary conditional logic in your workflow. You can set up one-to-many conditions to evaluate each item of data being inputted into the node. That data will either evaluate to TRUE or FALSE and route out of the node accordingly.\n\nThis node has multiple types of conditions: Bool, String, Number, and Date & Time.","resources":{"generic":[{"url":"https://n8n.io/blog/learn-to-automate-your-factorys-incident-reporting-a-step-by-step-guide/","icon":"🏭","label":"Learn to Automate Your Factory's Incident Reporting: A Step by Step Guide"},{"url":"https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/","icon":"☀️","label":"2021: The Year to Automate the New You with n8n"},{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/create-a-toxic-language-detector-for-telegram/","icon":"🤬","label":"Create a toxic language detector for Telegram in 4 step"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/","icon":"🔗","label":"How to build a low-code, self-hosted URL shortener in 3 steps"},{"url":"https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/","icon":"⚙️","label":"Automate your data processing pipeline in 9 steps"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/","icon":"⚡️","label":"5 tasks you can automate with the new Notion API "},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/automation-for-maintainers-of-open-source-projects/","icon":"🏷️","label":"How to automatically manage contributions to open-source projects"},{"url":"https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/","icon":" 🕸️","label":"How uProc scraped a multi-page website with a low-code workflow"},{"url":"https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/","icon":"🤖","label":"5 workflow automations for Mattermost that we love at n8n"},{"url":"https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/","icon":"🧠","label":"Why this Product Manager loves workflow automation with n8n"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/","icon":"🎡","label":"How to set up a no-code CI/CD pipeline with GitHub and TravisCI"},{"url":"https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/","icon":"🎖","label":"Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.if/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow"]}}},"group":"[\"transform\"]","defaults":{"name":"If","color":"#408000"},"iconData":{"icon":"map-signs","type":"icon"},"displayName":"If","typeVersion":2,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":24,"icon":"file:merge.svg","name":"n8n-nodes-base.merge","codex":{"data":{"alias":["Join","Concatenate","Wait"],"resources":{"generic":[{"url":"https://n8n.io/blog/how-to-sync-data-between-two-systems/","icon":"🏬","label":"How to synchronize data between two systems (one-way vs. two-way sync"},{"url":"https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/","icon":"🎫","label":"Supercharging your conference registration process with n8n"},{"url":"https://n8n.io/blog/migrating-community-metrics-to-orbit-using-n8n/","icon":"📈","label":"Migrating Community Metrics to Orbit using n8n"},{"url":"https://n8n.io/blog/build-your-own-virtual-assistant-with-n8n-a-step-by-step-guide/","icon":"👦","label":"Build your own virtual assistant with n8n: A step by step guide"},{"url":"https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/","icon":"🙌","label":"Sending Automated Congratulations with Google Sheets, Twilio, and n8n "},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.merge/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Merge"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTc3XzUxOCkiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgNDhDMCAyMS40OTAzIDIxLjQ5MDMgMCA0OCAwSDExMkMxMzguNTEgMCAxNjAgMjEuNDkwMyAxNjAgNDhWNTZIMTk2LjI1MkMyNDAuNDM1IDU2IDI3Ni4yNTIgOTEuODE3MiAyNzYuMjUyIDEzNlYxOTJDMjc2LjI1MiAyMTQuMDkxIDI5NC4xNjEgMjMyIDMxNi4yNTIgMjMySDM1MlYyMjRDMzUyIDE5Ny40OSAzNzMuNDkgMTc2IDQwMCAxNzZINDY0QzQ5MC41MSAxNzYgNTEyIDE5Ny40OSA1MTIgMjI0VjI4OEM1MTIgMzE0LjUxIDQ5MC41MSAzMzYgNDY0IDMzNkg0MDBDMzczLjQ5IDMzNiAzNTIgMzE0LjUxIDM1MiAyODhWMjgwSDMxNi4yNTJDMjk0LjE2MSAyODAgMjc2LjI1MiAyOTcuOTA5IDI3Ni4yNTIgMzIwVjM3NkMyNzYuMjUyIDQyMC4xODMgMjQwLjQzNSA0NTYgMTk2LjI1MiA0NTZIMTYwVjQ2NEMxNjAgNDkwLjUxIDEzOC41MSA1MTIgMTEyIDUxMkg0OEMyMS40OTAzIDUxMiAwIDQ5MC41MSAwIDQ2NFY0MDBDMCAzNzMuNDkgMjEuNDkwMyAzNTIgNDggMzUySDExMkMxMzguNTEgMzUyIDE2MCAzNzMuNDkgMTYwIDQwMFY0MDhIMTk2LjI1MkMyMTMuOTI1IDQwOCAyMjguMjUyIDM5My42NzMgMjI4LjI1MiAzNzZWMzIwQzIyOC4yNTIgMjk0Ljc4NCAyMzguODU5IDI3Mi4wNDQgMjU1Ljg1MyAyNTZDMjM4Ljg1OSAyMzkuOTU2IDIyOC4yNTIgMjE3LjIxNiAyMjguMjUyIDE5MlYxMzZDMjI4LjI1MiAxMTguMzI3IDIxMy45MjUgMTA0IDE5Ni4yNTIgMTA0SDE2MFYxMTJDMTYwIDEzOC41MSAxMzguNTEgMTYwIDExMiAxNjBINDhDMjEuNDkwMyAxNjAgMCAxMzguNTEgMCAxMTJWNDhaTTEwNCA0OEMxMDguNDE4IDQ4IDExMiA1MS41ODE3IDExMiA1NlYxMDRDMTEyIDEwOC40MTggMTA4LjQxOCAxMTIgMTA0IDExMkg1NkM1MS41ODE3IDExMiA0OCAxMDguNDE4IDQ4IDEwNFY1NkM0OCA1MS41ODE3IDUxLjU4MTcgNDggNTYgNDhIMTA0Wk00NTYgMjI0QzQ2MC40MTggMjI0IDQ2NCAyMjcuNTgyIDQ2NCAyMzJWMjgwQzQ2NCAyODQuNDE4IDQ2MC40MTggMjg4IDQ1NiAyODhINDA4QzQwMy41ODIgMjg4IDQwMCAyODQuNDE4IDQwMCAyODBWMjMyQzQwMCAyMjcuNTgyIDQwMy41ODIgMjI0IDQwOCAyMjRINDU2Wk0xMTIgNDA4QzExMiA0MDMuNTgyIDEwOC40MTggNDAwIDEwNCA0MDBINTZDNTEuNTgxNyA0MDAgNDggNDAzLjU4MiA0OCA0MDhWNDU2QzQ4IDQ2MC40MTggNTEuNTgxNyA0NjQgNTYgNDY0SDEwNEMxMDguNDE4IDQ2NCAxMTIgNDYwLjQxOCAxMTIgNDU2VjQwOFoiIGZpbGw9IiM1NEI4QzkiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTc3XzUxOCI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Merge","typeVersion":3,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":307,"icon":"file:s3.svg","name":"n8n-nodes-base.awsS3","codex":{"data":{"resources":{"generic":[{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/aws-workflow-automation/","label":"7 no-code workflow automations for Amazon Web Services"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.awss3/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/aws/"}]},"categories":["Development","Data & Storage"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"output\"]","defaults":{"name":"AWS S3"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDY1IiBoZWlnaHQ9IjI1MDAiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIiB2aWV3Qm94PSIwIDAgMjU2IDMxMCI+PHBhdGggZmlsbD0iIzhDMzEyMyIgZD0iTTIwLjYyNCA1My42ODYgMCA2NHYxODEuMDJsMjAuNjI0IDEwLjI1NC4xMjQtLjE0OVY1My44Mjh6Ii8+PHBhdGggZmlsbD0iI0UwNTI0MyIgZD0iTTEzMSAyMjkgMjAuNjI0IDI1NS4yNzRWNTMuNjg2TDEzMSA3OS4zODd6Ii8+PHBhdGggZmlsbD0iIzhDMzEyMyIgZD0ibTgxLjE3OCAxODcuODY2IDQ2LjgxOCA1Ljk2LjI5NC0uNjc4LjI2My03Ni43Ny0uNTU3LS42LTQ2LjgxOCA1Ljg3NHoiLz48cGF0aCBmaWxsPSIjOEMzMTIzIiBkPSJtMTI3Ljk5NiAyMjkuMjk1IDEwNy4zNzEgMjYuMDM1LjE2OS0uMjY5LS4wMDMtMjAxLjE5NS0uMTctLjE4LTEwNy4zNjcgMjUuOTk2eiIvPjxwYXRoIGZpbGw9IiNFMDUyNDMiIGQ9Im0xNzQuODI3IDE4Ny44NjYtNDYuODMxIDUuOTZ2LTc4LjA0OGw0Ni44MzEgNS44NzR6Ii8+PHBhdGggZmlsbD0iIzVFMUYxOCIgZD0ibTE3NC44MjcgODkuNjMxLTQ2LjgzMSA4LjUzNS00Ni44MTgtOC41MzUgNDYuNzU5LTEyLjI1NnoiLz48cGF0aCBmaWxsPSIjRjJCMEE5IiBkPSJtMTc0LjgyNyAyMTkuODAxLTQ2LjgzMS04LjU5MS00Ni44MTggOC41OTEgNDYuNzYxIDEzLjA1M3oiLz48cGF0aCBmaWxsPSIjOEMzMTIzIiBkPSJtODEuMTc4IDg5LjYzMSA0Ni44MTgtMTEuNTg2LjM3OS0uMTE3Vi4zMTNMMTI3Ljk5NiAwIDgxLjE3OCAyMy40MTN6Ii8+PHBhdGggZmlsbD0iI0UwNTI0MyIgZD0ibTE3NC44MjcgODkuNjMxLTQ2LjgzMS0xMS41ODZWMGw0Ni44MzEgMjMuNDEzeiIvPjxwYXRoIGZpbGw9IiM4QzMxMjMiIGQ9Im0xMjcuOTk2IDMwOS40MjgtNDYuODIzLTIzLjQwNXYtNjYuMjE3bDQ2LjgyMyAxMS41ODIuNjg5Ljc4My0uMTg3IDc1LjkwNnoiLz48cGF0aCBmaWxsPSIjRTA1MjQzIiBkPSJtMTI3Ljk5NiAzMDkuNDI4IDQ2LjgyNy0yMy40MDV2LTY2LjIxN2wtNDYuODI3IDExLjU4MnpNMjM1LjM2NyA1My42ODYgMjU2IDY0djE4MS4wMmwtMjAuNjMzIDEwLjMxeiIvPjwvc3ZnPg=="},"displayName":"AWS S3","typeVersion":2,"nodeCategories":[{"id":3,"name":"Data & Storage"},{"id":5,"name":"Development"}]},{"id":356,"icon":"file:gmail.svg","name":"n8n-nodes-base.gmail","codex":{"data":{"alias":["email","human","form","wait","hitl","approval"],"resources":{"generic":[{"url":"https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/","icon":"🧬","label":"Why business process automation with n8n can change your daily life"},{"url":"https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/","icon":"🎫","label":"Supercharging your conference registration process with n8n"},{"url":"https://n8n.io/blog/no-code-ecommerce-workflow-automations/","icon":"store","label":"6 e-commerce workflows to power up your Shopify s"},{"url":"https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/","icon":"👥","label":"How to get started with CRM automation (with 3 no-code workflow ideas"},{"url":"https://n8n.io/blog/automate-google-apps-for-productivity/","icon":"💡","label":"15 Google apps you can combine and automate to increase productivity"},{"url":"https://n8n.io/blog/your-business-doesnt-need-you-to-operate/","icon":" 🖥️","label":"Hey founders! Your business doesn't need you to operate"},{"url":"https://n8n.io/blog/using-automation-to-boost-productivity-in-the-workplace/","icon":"💪","label":"Using Automation to Boost Productivity in the Workplace"}],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.gmail/"}],"credentialDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/credentials/google/oauth-single-service/"}]},"categories":["Communication","HITL"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"HITL":["Human in the Loop"]}}},"group":"[\"transform\"]","defaults":{"name":"Gmail"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTYiIGhlaWdodD0iMTkzIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZmlsbD0iIzQyODVGNCIgZD0iTTU4LjE4MiAxOTIuMDVWOTMuMTRMMjcuNTA3IDY1LjA3NyAwIDQ5LjUwNHYxMjUuMDkxYzAgOS42NTggNy44MjUgMTcuNDU1IDE3LjQ1NSAxNy40NTV6Ii8+PHBhdGggZmlsbD0iIzM0QTg1MyIgZD0iTTE5Ny44MTggMTkyLjA1aDQwLjcyN2M5LjY1OSAwIDE3LjQ1NS03LjgyNiAxNy40NTUtMTcuNDU1VjQ5LjUwNWwtMzEuMTU2IDE3LjgzNy0yNy4wMjYgMjUuNzk4eiIvPjxwYXRoIGZpbGw9IiNFQTQzMzUiIGQ9Im01OC4xODIgOTMuMTQtNC4xNzQtMzguNjQ3IDQuMTc0LTM2Ljk4OUwxMjggNjkuODY4bDY5LjgxOC01Mi4zNjQgNC42NyAzNC45OTItNC42NyA0MC42NDRMMTI4IDE0NS41MDR6Ii8+PHBhdGggZmlsbD0iI0ZCQkMwNCIgZD0iTTE5Ny44MTggMTcuNTA0VjkzLjE0TDI1NiA0OS41MDRWMjYuMjMxYzAtMjEuNTg1LTI0LjY0LTMzLjg5LTQxLjg5LTIwLjk0NXoiLz48cGF0aCBmaWxsPSIjQzUyMjFGIiBkPSJtMCA0OS41MDQgMjYuNzU5IDIwLjA3TDU4LjE4MiA5My4xNFYxNy41MDRMNDEuODkgNS4yODZDMjQuNjEtNy42NiAwIDQuNjQ2IDAgMjYuMjN6Ii8+PC9zdmc+"},"displayName":"Gmail","typeVersion":2,"nodeCategories":[{"id":6,"name":"Communication"},{"id":28,"name":"HITL"}]},{"id":565,"icon":"fa:sticky-note","name":"n8n-nodes-base.stickyNote","codex":{"data":{"alias":["Comments","Notes","Sticky"],"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers"]}}},"group":"[\"input\"]","defaults":{"name":"Sticky Note","color":"#FFD233"},"iconData":{"icon":"sticky-note","type":"icon"},"displayName":"Sticky Note","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":834,"icon":"file:code.svg","name":"n8n-nodes-base.code","codex":{"data":{"alias":["cpde","Javascript","JS","Python","Script","Custom Code","Function"],"details":"The Code node allows you to execute JavaScript in your workflow.","resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/"}]},"categories":["Development","Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Helpers","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Code"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTcxXzQ0MSkiPgo8cGF0aCBkPSJNMTcwLjI4MyA0OEgxOTYuNUMyMDMuMTI3IDQ4IDIwOC41IDQyLjYyNzQgMjA4LjUgMzZWMTJDMjA4LjUgNS4zNzI1OCAyMDMuMTI3IDAgMTk2LjUgMEgxNzAuMjgzQzEyNi4xIDAgOTAuMjgzIDM1LjgxNzIgOTAuMjgzIDgwVjE3NkM5MC4yODMgMjA2LjkyOCA2NS4yMTA5IDIzMiAzNC4yODMgMjMySDIzQzE2LjM3MjYgMjMyIDExIDIzNy4zNzIgMTEgMjQ0VjI2OEMxMSAyNzQuNjI3IDE2LjM3MjQgMjgwIDIyLjk5OTYgMjgwTDM0LjI4MyAyODBDNjUuMjEwOSAyODAgOTAuMjgzIDMwNS4wNzIgOTAuMjgzIDMzNlY0NDBDOTAuMjgzIDQ3OS43NjQgMTIyLjUxOCA1MTIgMTYyLjI4MyA1MTJIMTk2LjVDMjAzLjEyNyA1MTIgMjA4LjUgNTA2LjYyNyAyMDguNSA1MDBWNDc2QzIwOC41IDQ2OS4zNzMgMjAzLjEyNyA0NjQgMTk2LjUgNDY0SDE2Mi4yODNDMTQ5LjAyOCA0NjQgMTM4LjI4MyA0NTMuMjU1IDEzOC4yODMgNDQwVjMzNkMxMzguMjgzIDMwOS4wMjIgMTI4LjAxMSAyODQuNDQzIDExMS4xNjQgMjY1Ljk2MUMxMDYuMTA5IDI2MC40MTYgMTA2LjEwOSAyNTEuNTg0IDExMS4xNjQgMjQ2LjAzOUMxMjguMDExIDIyNy41NTcgMTM4LjI4MyAyMDIuOTc4IDEzOC4yODMgMTc2VjgwQzEzOC4yODMgNjIuMzI2OSAxNTIuNjEgNDggMTcwLjI4MyA0OFoiIGZpbGw9IiNGRjk5MjIiLz4KPHBhdGggZD0iTTMwNSAzNkMzMDUgNDIuNjI3NCAzMTAuMzczIDQ4IDMxNyA0OEgzNDIuOTc5QzM2MC42NTIgNDggMzc0Ljk3OCA2Mi4zMjY5IDM3NC45NzggODBWMTc2QzM3NC45NzggMjAyLjk3OCAzODUuMjUxIDIyNy41NTcgNDAyLjA5OCAyNDYuMDM5QzQwNy4xNTMgMjUxLjU4NCA0MDcuMTUzIDI2MC40MTYgNDAyLjA5OCAyNjUuOTYxQzM4NS4yNTEgMjg0LjQ0MyAzNzQuOTc4IDMwOS4wMjIgMzc0Ljk3OCAzMzZWNDMyQzM3NC45NzggNDQ5LjY3MyAzNjAuNjUyIDQ2NCAzNDIuOTc5IDQ2NEgzMTdDMzEwLjM3MyA0NjQgMzA1IDQ2OS4zNzMgMzA1IDQ3NlY1MDBDMzA1IDUwNi42MjcgMzEwLjM3MyA1MTIgMzE3IDUxMkgzNDIuOTc5QzM4Ny4xNjEgNTEyIDQyMi45NzggNDc2LjE4MyA0MjIuOTc4IDQzMlYzMzZDNDIyLjk3OCAzMDUuMDcyIDQ0OC4wNTEgMjgwIDQ3OC45NzkgMjgwSDQ5MEM0OTYuNjI3IDI4MCA1MDIgMjc0LjYyOCA1MDIgMjY4VjI0NEM1MDIgMjM3LjM3MyA0OTYuNjI4IDIzMiA0OTAgMjMyTDQ3OC45NzkgMjMyQzQ0OC4wNTEgMjMyIDQyMi45NzggMjA2LjkyOCA0MjIuOTc4IDE3NlY4MEM0MjIuOTc4IDM1LjgxNzIgMzg3LjE2MSAwIDM0Mi45NzkgMEgzMTdDMzEwLjM3MyAwIDMwNSA1LjM3MjU4IDMwNSAxMlYzNloiIGZpbGw9IiNGRjk5MjIiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTcxXzQ0MSI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="},"displayName":"Code","typeVersion":2,"nodeCategories":[{"id":5,"name":"Development"},{"id":9,"name":"Core Nodes"}]},{"id":839,"icon":"fa:clock","name":"n8n-nodes-base.scheduleTrigger","codex":{"data":{"alias":["Time","Scheduler","Polling","Cron","Interval"],"resources":{"generic":[],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.scheduletrigger/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0"}},"group":"[\"trigger\",\"schedule\"]","defaults":{"name":"Schedule Trigger","color":"#31C49F"},"iconData":{"icon":"clock","type":"icon"},"displayName":"Schedule Trigger","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":844,"icon":"fa:filter","name":"n8n-nodes-base.filter","codex":{"data":{"alias":["Router","Filter","Condition","Logic","Boolean","Branch"],"details":"The Filter node can be used to filter items based on a condition. If the condition is met, the item will be passed on to the next node. If the condition is not met, the item will be omitted. Conditions can be combined together by AND(meet all conditions), or OR(meet at least one condition).","resources":{"generic":[],"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.filter/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Flow","Data Transformation"]}}},"group":"[\"transform\"]","defaults":{"name":"Filter","color":"#229eff"},"iconData":{"icon":"filter","type":"icon"},"displayName":"Filter","typeVersion":2,"nodeCategories":[{"id":9,"name":"Core Nodes"}]},{"id":1235,"icon":"file:extractFromFile.svg","name":"n8n-nodes-base.extractFromFile","codex":{"data":{"alias":["CSV","Spreadsheet","Excel","xls","xlsx","ods","tabular","decode","decoding","Move Binary Data","Binary","File","PDF","JSON","HTML","ICS","iCal","txt","Text","RTF","XML","64","Base64","Convert"],"resources":{"primaryDocumentation":[{"url":"https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.extractfromfile/"}]},"categories":["Core Nodes"],"nodeVersion":"1.0","codexVersion":"1.0","subcategories":{"Core Nodes":["Files","Data Transformation"]}}},"group":"[\"input\"]","defaults":{"name":"Extract from File"},"iconData":{"type":"file","fileBuffer":"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAuOTM3NSAyQzAuNDE5NzMzIDIgMCAyLjQxOTczIDAgMi45Mzc1VjM3LjMyMjFDMCAzNy44Mzk5IDAuNDE5NzMzIDM4LjI1OTYgMC45Mzc1IDM4LjI1OTZIMjYuMjE1NEMyNi43MzMyIDM4LjI1OTYgMjcuMTUyOSAzNy44Mzk5IDI3LjE1MjkgMzcuMzIyMUwyNy4xNTI5IDMwLjY3MTlMMTYuNzk2OSAzMC42NzE5QzE0Ljg5ODQgMzAuNjcxOSAxMy4zNTk0IDI5LjEzMjkgMTMuMzU5NCAyNy4yMzQ0VjI1LjM1OTRDMTMuMzU5NCAyMy40NjA5IDE0Ljg5ODQgMjEuOTIxOSAxNi43OTY5IDIxLjkyMTlIMjcuMTUyOUwyNy4xNTI5IDE1Ljc4MjFIMTQuMzA4M0MxMy43OTA2IDE1Ljc4MjEgMTMuMzcwOCAxNS4zNjI0IDEzLjM3MDggMTQuODQ0NlYySDAuOTM3NVoiIGZpbGw9IiMzNTNGNkUiLz4KPHBhdGggZD0iTTE2LjAyNzEgMkMxNS45NDA4IDIgMTUuODcwOCAyLjA2OTk2IDE1Ljg3MDggMi4xNTYyNVYxMi44MTM0QzE1Ljg3MDggMTMuMDcyMyAxNi4wODA3IDEzLjI4MjEgMTYuMzM5NiAxMy4yODIxSDI2Ljk5NjdDMjcuMDgzIDEzLjI4MjEgMjcuMTUyOSAxMy4yMTIyIDI3LjE1MjkgMTMuMTI1OUwyNy4xNTI5IDEyLjYxNzFDMjcuMTUyOSAxMi4zNjg4IDI3LjA1NDUgMTIuMTMwNyAyNi44NzkxIDExLjk1NUwxNy4yMjI1IDIuMjc1MzhDMTcuMDQ2NiAyLjA5OTA4IDE2LjgwNzkgMiAxNi41NTg4IDJIMTYuMDI3MVoiIGZpbGw9IiMzNTNGNkUiLz4KPHBhdGggZD0iTTI5Ljc2NDIgMzQuNjUwM0MyOS4wMzQgMzMuOTE2IDI5LjAzNzQgMzIuNzI4OCAyOS43NzE2IDMxLjk5ODZMMzMuNjE5NyAyOC4xNzE5TDE2Ljc5NjkgMjguMTcxOUMxNi4yNzkxIDI4LjE3MTkgMTUuODU5NCAyNy43NTIxIDE1Ljg1OTQgMjcuMjM0NFYyNS4zNTk0QzE1Ljg1OTQgMjQuODQxNiAxNi4yNzkxIDI0LjQyMTkgMTYuNzk2OSAyNC40MjE5TDMzLjU0MTIgMjQuNDIxOUwyOS43NzE2IDIwLjY3MzNDMjkuMDM3NCAxOS45NDMxIDI5LjAzNCAxOC43NTU5IDI5Ljc2NDIgMTguMDIxNkMzMC40OTQ0IDE3LjI4NzQgMzEuNjgxNiAxNy4yODQgMzIuNDE1OSAxOC4wMTQyTDM5LjQ0NzEgMjUuMDA2NEMzOS44MDEgMjUuMzU4MyA0MCAyNS44MzY4IDQwIDI2LjMzNTlDNDAgMjYuODM1IDM5LjgwMSAyNy4zMTM1IDM5LjQ0NzEgMjcuNjY1NUwzMi40MTU5IDM0LjY1NzZDMzEuNjgxNiAzNS4zODc4IDMwLjQ5NDQgMzUuMzg0NSAyOS43NjQyIDM0LjY1MDNaIiBmaWxsPSIjMzUzRjZFIi8+Cjwvc3ZnPgo="},"displayName":"Extract from File","typeVersion":1,"nodeCategories":[{"id":9,"name":"Core Nodes"}]}],"categories":[{"id":35,"name":"Document Extraction"},{"id":49,"name":"AI Summarization"}],"image":[]}}