{
  "id": "hAr5yxUjH6GnWhRq",
  "meta": {
    "instanceId": "efbf05d03d3e47a9d323e384711f6bc5e7df14be0284d04bab068a214f13aa2e",
    "templateCredsSetupCompleted": true
  },
  "name": "2-Utility-SaveInvoices_template",
  "tags": [
    {
      "id": "gZvMl56PRMiUM0GB",
      "name": "Utility",
      "createdAt": "2026-01-13T18:54:25.559Z",
      "updatedAt": "2026-01-13T18:54:25.559Z"
    },
    {
      "id": "vghJqYF0cZvWjgCI",
      "name": "Mail",
      "createdAt": "2026-01-13T18:54:25.550Z",
      "updatedAt": "2026-01-13T18:54:25.550Z"
    },
    {
      "id": "r7fcfwCl5k0mTYj8",
      "name": "PROD",
      "createdAt": "2026-01-13T18:54:59.745Z",
      "updatedAt": "2026-01-13T18:54:59.745Z"
    },
    {
      "id": "PlxhyZPXjXMiEkoD",
      "name": "creator-published",
      "createdAt": "2026-01-13T18:54:59.705Z",
      "updatedAt": "2026-01-13T18:54:59.705Z"
    }
  ],
  "nodes": [
    {
      "id": "8740476c-9d06-414b-b68a-828df048b29e",
      "name": "AI_Agent-fields",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2736,
        672
      ],
      "parameters": {
        "text": "=- You must respond ONLY with valid raw rendered JSON.\n- Do NOT include the word \"json\".\n- Do NOT include the word \"```json\".\n- Do NOT use triple backticks or markdown formatting.\n- Do NOT wrap the response in any key like \"output\".\n- Do NOT write anything starting at output directly start with valid root-level JSON.\n- Only respond with a valid, root-level JSON object.\n- Do NOT skip any line item. Continue extracting all line items until the sum of all line_total values exactly equals the total sale amount extracted from the invoice. This verification ensures that all items are fully extracted and no entries are missed. If the totals do not match, keep parsing and extracting additional line items until they do. Only then stop.\n\nText to convert: {{ $json.text }}",
        "options": {
          "systemMessage": "You are a document parsing assistant designed to extract structured data from invoice PDFs for automated uploading and validation in a financial system.\n\nExtract the following fields from the invoice text:\n\ninvoice_number: Extract from the 'Invoice No' field.\n\nvendor_name: Company name issuing the invoice.\n\ninvoice_date: Format as DD/MM/YYYY.\n\npo_number: Extract the PO number or return null if not found.\n\npo_date: Extract the PO date in DD/MM/YYYY format or return null if not found.\n\ntotal_amount: Extract the invoice total as a float.\n\ntax_details: Include total CGST, total SGST.\n\nline_items: List of all items in the invoice. For each item, extract:\n\n  Serial No.: Item Serial No. In invoice.\n\n  code: The TWW word and its postfix only (e.g., TWW, TWW-Cover, TWW-HPCN). Do not include HSN code or numbers. This field must always be present.\n\n  description: Item description, never include HSN code of product in product description.\n\n  last character: This is a single word/character string found just after or around the line item, typically at the end of the price line or just on the next line. It will never be a number, HSN code, GST %, or unit (like PCS). Examples: G, C, S, 5C, .C If nothing is present (only digits or blank), return \"\". Check the next 1–2 lines after each item if it's not found on the same line.\n\n  quantity: Quantity value.\n\n  unit_price: Price per unit.\n\n  line_total: Total price for the line.\n\n  hsn_code: HSN code of the item.\n\n  cgst: Only extract the CGST percentage (e.g., 9%, 6%) as written in the invoice. Do not calculate based on line total.\n\n  sgst: Only extract the SGST percentage (e.g., 9%, 6%) as written in the invoice. Do not calculate based on line total.\n\nImportant: Double-check that all line items are extracted without omission.\n\nDo NOT skip any line item. Continue extracting all line items until the sum of all line_total values exactly equals the total_amount extracted from the invoice. This verification ensures that all items are fully extracted and no entries are missed. If the totals do not match, keep parsing and extracting additional line items until they do. Only then stop."
        },
        "promptType": "define"
      },
      "typeVersion": 2.2
    },
    {
      "id": "f6d0666f-ba52-4ce9-a8bb-b6ea735427ca",
      "name": "Code_extractFields",
      "type": "n8n-nodes-base.code",
      "position": [
        3024,
        672
      ],
      "parameters": {
        "jsCode": "const output = $input.first().json.output;\nlet raw = output.result || output.completion || output.text || JSON.stringify(output);\n\n// Step 1: Remove backticks if present\nraw = raw.trim();\nif (raw.startsWith(\"```json\")) {\n  raw = raw.replace(/^```json\\s*/, '').replace(/```$/, '').trim();\n} else if (raw.startsWith(\"```\")) {\n  raw = raw.replace(/^```\\s*/, '').replace(/```$/, '').trim();\n}\n\n// Step 2: Find full JSON block from first { to last }\nconst start = raw.indexOf('{');\nconst end = raw.lastIndexOf('}');\nif (start === -1 || end === -1 || end <= start) {\n  return [{\n    json: {\n      error: \"No valid JSON block found\",\n      raw_output: raw\n    }\n  }];\n}\n\nlet jsonCandidate = raw.substring(start, end + 1).trim();\n\n// Step 3: Unescape characters\njsonCandidate = jsonCandidate\n  .replace(/\\\\n/g, '')\n  .replace(/\\\\t/g, '')\n  .replace(/\\\\\"/g, '\"')\n  .replace(/\\\\'/g, \"'\")\n  .replace(/\\\\\\\\/g, '\\\\');\n\n// Step 4: Parse JSON safely\ntry {\n  let parsed = JSON.parse(jsonCandidate);\n\n  // Only double-parse if it looks like stringified JSON\n  if (typeof parsed === \"string\" && parsed.trim().startsWith('{') && parsed.trim().endsWith('}')) {\n    parsed = JSON.parse(parsed);\n  }\n\n  return [{ json: parsed }];\n} catch (e) {\n  return [{\n    json: {\n      error: \"JSON parsing failed\",\n      raw_output: raw,\n      attempted_extraction: jsonCandidate,\n      message: e.message\n    }\n  }];\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "e308190d-ab88-430c-ac06-5835edbf0d7f",
      "name": "GoogleSheets_save",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3216,
        672
      ],
      "parameters": {
        "columns": {
          "value": {
            "Data": "={{ $json.invoice_date }}",
            "Tipo": "={{ $json.line_items[0].description }}",
            "Importo": "={{ $json.total_amount }}",
            "Fornitore": "={{ $json.vendor_name }}"
          },
          "schema": [
            {
              "id": "Fornitore",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Fornitore",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Tipo",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Tipo",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Data",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Data",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Importo",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Importo",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1842419144,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1-qkYalPWAugIUGqsgR8_gU15CHkzsqQfCVB793i8Ku8/edit#gid=1842419144",
          "cachedResultName": "Fatture-luce"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1-qkYalPWAugIUGqsgR8_gU15CHkzsqQfCVB793i8Ku8",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1-qkYalPWAugIUGqsgR8_gU15CHkzsqQfCVB793i8Ku8/edit?usp=drivesdk",
          "cachedResultName": "n8n"
        },
        "authentication": "serviceAccount"
      },
      "typeVersion": 4.7
    },
    {
      "id": "3c478faf-ec04-4da4-a7d4-34a208689ab0",
      "name": "downloadFile",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2352,
        208
      ],
      "parameters": {
        "url": "={{ $json.webContentLink }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "cb8e5ab0-cb78-4c18-88cc-6301ce5a00db",
      "name": "GoogleDrive-upload-file",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        2144,
        208
      ],
      "parameters": {
        "name": "={{ $json.from.value[0].name}}-{{ $json.date }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "=YOUR_ID"
        },
        "inputDataFieldName": "={{ $binary.keys()[0] }}"
      },
      "typeVersion": 3
    },
    {
      "id": "4966a033-d088-4ef1-88d8-b338b3a912b2",
      "name": "Gmail-Get_Invoice",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1936,
        208
      ],
      "webhookId": "a430c91c-4c1a-4377-88ad-f3ffefdc70ac",
      "parameters": {
        "simple": false,
        "options": {
          "downloadAttachments": true
        },
        "messageId": "={{ $node[\"Get many messages\"].json.id }}",
        "operation": "get"
      },
      "typeVersion": 2.1
    },
    {
      "id": "b45ff76f-eab8-4989-9c0f-3e965b6b2aee",
      "name": "Delete a message",
      "type": "n8n-nodes-base.gmail",
      "position": [
        3168,
        208
      ],
      "webhookId": "c974ebf7-e5f5-4e25-a894-d16561876f46",
      "parameters": {
        "messageId": "={{ $('Get many messages').item.json.id }}",
        "operation": "delete"
      },
      "typeVersion": 2.1
    },
    {
      "id": "7f91a088-b772-4cf0-9460-3d8b1f907286",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        672,
        192
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "triggerAtMinute": 25
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "06bbcd36-63f8-4675-8844-50d0f028efa2",
      "name": "Get many messages",
      "type": "n8n-nodes-base.gmail",
      "position": [
        928,
        192
      ],
      "webhookId": "af615cf7-1086-4de1-b3a6-020e6f96bcf1",
      "parameters": {
        "simple": false,
        "filters": {
          "sender": "SENDER_EMAIL_ADDRESS",
          "receivedAfter": "={{ new Date(Date.now() - 60 * 60 * 1000).toISOString() }}"
        },
        "options": {
          "downloadAttachments": true
        },
        "operation": "getAll"
      },
      "typeVersion": 2.1
    },
    {
      "id": "b8777576-41e7-46c0-86b8-7af73f30ab5c",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -128,
        -48
      ],
      "parameters": {
        "width": 752,
        "height": 720,
        "content": "## Automated Invoice Archiving - overview\n\nThis workflow automatically collects invoice emails from your ISP/utility provider, saves attached PDFs to Google Drive (and optionally SFTP), extracts key invoice data using AI, and logs them into Google Sheets.\n\nRead: [Full setup Guide](https://paoloronco.it/n8n-template-automated-invoice-archiving/)\n\n### How it Works (High-Level)\n\n1. **Trigger** – Runs at your chosen interval.\n2. **Email Filter** – Fetches only messages from the sender you specify and only if they include PDF attachments.\n3. **Download Invoice** – Retrieves the email and PDF file.\n4. **Save to Drive** – Uploads the PDF to a selected Google Drive folder.\n    (Optional) SFTP Upload – Sends a copy to your server.\n5. **Extract Text** – Converts the PDF to text.\n6. **AI Parsing** – Extracts invoice fields (vendor, date, total, line items, taxes, etc.).\n7. **Cleanup** (Optional) – Delete email and/or Google Drive temp file.\n8. **Append to Sheets** – Saves selected fields into your Google Sheet.\n\n### Tips\n\n- Works best with text-based PDFs.\n- SFTP, Gmail deletion, and Drive deletion are optional.\n- You can use different senders for different providers.\n- Sheets can be used to chart or monitor consumption over time.\n\n### Important\n\nInvoices may contain sensitive data.\nUse secure storage, APIs, and accounts."
      },
      "typeVersion": 1
    },
    {
      "id": "22841f63-b10a-48bd-bf8e-fbd51a65beac",
      "name": "Extract from File1",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        2560,
        416
      ],
      "parameters": {
        "options": {},
        "operation": "pdf"
      },
      "typeVersion": 1
    },
    {
      "id": "f34ed231-eb4e-4c6d-afe4-55002ec51d8f",
      "name": "OpenRouter Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        2736,
        816
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "f544762a-4d82-4638-99f5-dd16cf233bc0",
      "name": "Delete a file1",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        2944,
        208
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('GoogleDrive-upload-file').item.json.id }}"
        },
        "options": {},
        "operation": "deleteFile"
      },
      "typeVersion": 3
    },
    {
      "id": "95b99314-b4c1-4b3b-af12-ac5ee64cff06",
      "name": "Filter-contains_attachment",
      "type": "n8n-nodes-base.filter",
      "position": [
        1168,
        192
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "87e927b3-4f28-4843-b2d0-cf7269437fca",
              "operator": {
                "type": "object",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $('Get many messages').item.binary}}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "5a407aa3-d3d1-4f45-9c4a-88a5dd0cfdb7",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        -48
      ],
      "parameters": {
        "color": 7,
        "height": 400,
        "content": "### SECTION 1 Trigger\n\nRuns the workflow on a schedule you choose. Adjust interval as needed (hourly, daily, etc.)."
      },
      "typeVersion": 1
    },
    {
      "id": "163f9fb4-9dc1-4e92-83ee-ecc3e1510585",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        896,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 400,
        "content": "### SECTION 2.1 — Email Intake & Attachment filtering\n\nFetches messages from a specific sender and filters only emails containing PDF attachments.\n\n**Setup:**\nIn **Gmail → Get Many Messages**, enter the sender email that sends you invoices.\n\nConfigure your Gmail OAuth2 account: [🔗 Gmail Node Docs](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.gmail/)"
      },
      "typeVersion": 1
    },
    {
      "id": "31b2113c-b7f7-45f2-b277-1b3800c8c68d",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2096,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 816,
        "height": 416,
        "content": "### SECTION 3 — Download & Storage\n\nDownloads the invoice PDF and saves it to Google Drive. You can optionally upload a copy to your FTP/SFTP server.\n\n**Google Drive:**\nConnect your Drive account: [🔗 Google Drive Docs](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googledrive/)\nInsert the **Folder ID** where invoices will be stored.\n\n**FTP/SFTP (optional):**\nSet up your FTP/SFTP credentials: [🔗 FTP Node Docs](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.ftp/)\nIn **PATH**, enter the directory where files should be saved. Leave other fields unchanged.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c633ca42-aeb2-42d9-9a53-6f64ed13a5ad",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1792,
        400
      ],
      "parameters": {
        "color": 7,
        "width": 896,
        "content": "### SECTION 4 — PDF Text Extraction\n\nConverts the PDF into text before sending it to the AI model. OCR not required unless the PDF is image-only."
      },
      "typeVersion": 1
    },
    {
      "id": "08b97c66-9033-4b59-aa9f-2be21f09fee6",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2704,
        400
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 544,
        "content": "### SECTION 5 — AI Parsing\n\nThe AI Agent extracts structured data (date, vendor, amount, items). The Code node cleans the JSON.\n\n**Setup**\n Enter your LLM provider API key (OpenRouter/OpenAI).\n Ensure you have available credits.\n Select your preferred model (GPT-4.1, Llama 3, etc.).\n No additional configuration needed.\n\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "34bfa6d9-f7ea-4acb-95f6-1c57bc6a89d2",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3168,
        400
      ],
      "parameters": {
        "color": 7,
        "width": 352,
        "height": 544,
        "content": "### SECTION 6 - GoogleSheets Logging\n\n**Setup**\nConnect your Google Sheets Service Account.\nSet **Document ID** and **Sheet Name**.\nCreate these columns in the first row:\n\n* **Vendor**\n* **Type**\n* **Date**\n* **Amount**\n\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b464e077-d9e3-4100-b6f5-71c5c3117f9a",
      "name": "Code in JavaScript",
      "type": "n8n-nodes-base.code",
      "position": [
        2560,
        208
      ],
      "parameters": {
        "jsCode": "return $input.all().map(item => {\n  if (!item.binary) return item;\n\n  const key = Object.keys(item.binary)[0];         // prende il primo binary field\n  const bin = item.binary[key];\n\n  let n = item.json.name || bin?.fileName || 'invoice';\n\n  n = n.replace(/[\\x00-\\x1F\\x7F]/g, '');\n  n = n.replace(/\\.\\d+Z$/, '');\n  n = n.replace(/[:<>\"/\\\\|?*]/g, '-');\n  n = n.replace(/\\s+/g, ' ').replace(/[. ]+$/, '');\n  if (!n.toLowerCase().endsWith('.pdf')) n += '.pdf';\n\n  item.binary[key].fileName = n;\n  return item;\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "7f948036-e8a3-4c33-a934-c0e6dcdc5bc6",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        1680,
        192
      ],
      "parameters": {},
      "typeVersion": 2.3
    },
    {
      "id": "c993184f-56b6-4c05-9868-963917d2dea7",
      "name": "Check: Message already processed",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1520,
        192
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('Get many messages').item.json.id }}",
              "lookupColumn": "uniqueKey"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "=12345678"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1-qkYalPWAugIUGqsgR8_gU15CHkzsqQfCVB793i8Ku8",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1-qkYalPWAugIUGqsgR8_gU15CHkzsqQfCVB793i8Ku8/edit?usp=drivesdk",
          "cachedResultName": "n8n"
        },
        "authentication": "serviceAccount"
      },
      "typeVersion": 4.7,
      "alwaysOutputData": true
    },
    {
      "id": "9f7b972a-3ee1-4ba0-bb35-145aef6efbef",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 416,
        "content": "### Section 3: Check if already existing\n\nPrevents duplicate processing of the same email.\nThis section checks whether the current Gmail message has already been processed by looking up its Gmail message ID in a Google Sheets registry.\n\nConfiguration\n* Set the Google Sheets document and sheet name.\n* Ensure the sheet has a uniqueKey column containing Gmail message IDs.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "08bbcd5d-dc15-48bd-9d29-a6bb8316364b",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1904,
        -48
      ],
      "parameters": {
        "color": 7,
        "width": 176,
        "height": 416,
        "content": "### Section 2.2\n\nGet attachment from Gmail \n(binary file)"
      },
      "typeVersion": 1
    },
    {
      "id": "114f1c8c-c5de-4f09-ab00-53acff498026",
      "name": "FTP-upload",
      "type": "n8n-nodes-base.ftp",
      "position": [
        2752,
        208
      ],
      "parameters": {
        "path": "=PATH/PATH/{{ $binary[Object.keys($binary)[0]].fileName }}",
        "protocol": "sftp",
        "operation": "upload"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "54ea6eaf-4f29-4b70-9b79-20cf3a309300",
  "connections": {
    "FTP-upload": {
      "main": [
        [
          {
            "node": "Delete a file1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "downloadFile": {
      "main": [
        [
          {
            "node": "Extract from File1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Delete a file1": {
      "main": [
        [
          {
            "node": "Delete a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI_Agent-fields": {
      "main": [
        [
          {
            "node": "Code_extractFields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get many messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get many messages": {
      "main": [
        [
          {
            "node": "Filter-contains_attachment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail-Get_Invoice": {
      "main": [
        [
          {
            "node": "GoogleDrive-upload-file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "FTP-upload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code_extractFields": {
      "main": [
        [
          {
            "node": "GoogleSheets_save",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File1": {
      "main": [
        [
          {
            "node": "AI_Agent-fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "AI_Agent-fields",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "GoogleDrive-upload-file": {
      "main": [
        [
          {
            "node": "downloadFile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter-contains_attachment": {
      "main": [
        [
          {
            "node": "Check: Message already processed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}