{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "87d0fa7b-1a42-4f48-a942-9e8ff3ab4823",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        420,
        360
      ],
      "parameters": {
        "jsonSchema": "{\n \"PO date\": { \"type\": \"date\" },\n \"PO number\": { \"type\": \"string\" },\n \"PO release date\": { \"type\": \"string\" },\n \"Supplier name\": { \"type\": \"string\" },\n \"Billing address\": {\n \"type\": \"object\",\n \"properties\": {\n \"address 1\": { \"type\": \"string\" },\n \"address 2\": { \"type\": \"string\" },\n \"city\": { \"type\": \"string\" },\n \"postcode\": { \"type\": \"string\" }\n }\n },\n \"GSTINnumber\": { \"type\": \"string\" }, \n \"Shipping addresses\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"address 1\": { \"type\": \"string\" },\n \"address 2\": { \"type\": \"string\" },\n \"city\": { \"type\": \"string\" },\n \"postcode\": { \"type\": \"string\" }\n }\n }\n },\n \"Line items\": {\n \"type\": \"array\",\n \"items\": {\n \"name\": \"string\",\n \"description\": \"string\",\n \"Quantity\": \"number\",\n \"MRP\": \"number\",\n \"CGST Amount\": \"number\",\n \"SGST Amount\": \"number\", \n \"IGST Amount\": \"number\",\n \"Total\": \"number\"\n }\n },\n \"Total Amount \": { \"type\": \"number\" },\n \"Total Tax\": { \"type\": \"number\" },\n \"Grand Total\": { \"type\": \"number\" }\n}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "9e253f1c-f58c-4ad0-a105-8fc65825b749",
      "name": "Upload to LlamaParse",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -560,
        300
      ],
      "parameters": {
        "url": "https://api.cloud.llamaindex.ai/api/parsing/upload",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "multipart-form-data",
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "file",
              "parameterType": "formBinaryData",
              "inputDataFieldName": "attachment_0"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8654de08-2169-4014-a320-f141b341f367",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -640,
        -40
      ],
      "parameters": {
        "color": 7,
        "width": 786,
        "height": 703,
        "content": "## 2. Advanced PDF Processing with LlamaParse\n[Read more about using HTTP Requests](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/)\n\nLlamaIndex's LlamaCloud is a cloud-based service that allows you to upload,\nparse, and index document. LlamaParse is a tool offered by LlamaCloud\nto parse for complex PDFs with embedded objects ie PDF Tables and figures.\n\nAt time of writing, you can parse 1000 pdfs/day with LlamaCloud's free plan\nby signing up at [https://cloud.llamaindex.ai/](https://cloud.llamaindex.ai/?ref=n8n.io)."
      },
      "typeVersion": 1
    },
    {
      "id": "dac79610-f778-4772-a667-5f3d1afd5cd1",
      "name": "Receiving Invoices",
      "type": "n8n-nodes-base.gmailTrigger",
      "position": [
        -1400,
        280
      ],
      "parameters": {
        "simple": false,
        "filters": {
          "q": "has:attachment",
          "sender": "user@example.com"
        },
        "options": {
          "downloadAttachments": true
        },
        "pollTimes": {
          "item": [
            {
              "mode": "everyHour"
            }
          ]
        }
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "8f8b951f-401c-4e10-a879-7b8f12aa1138",
      "name": "Append to Reconciliation Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        780,
        200
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "PO No.",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "PO No.",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PO Date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "PO Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PO ",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "PO ",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hxo6lJet0f49S0FyDDWnm5u0GuWInHvUFAji3pcGeEk/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1hxo6lJet0f49S0FyDDWnm5u0GuWInHvUFAji3pcGeEk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1hxo6lJet0f49S0FyDDWnm5u0GuWInHvUFAji3pcGeEk/edit?usp=drivesdk",
          "cachedResultName": "Invoice Details"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "c0489efe-133d-4176-bb6a-b052a6acf25e",
      "name": "Get Processing Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -380,
        300
      ],
      "parameters": {
        "url": "=https://api.cloud.llamaindex.ai/api/parsing/job/{{ $json.id }}",
        "options": {},
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4103a5e3-da0c-4c58-9310-a0874ce39dc8",
      "name": "Wait to stay within service limits",
      "type": "n8n-nodes-base.wait",
      "position": [
        -60,
        440
      ],
      "parameters": {
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "40ad3a55-0324-4ced-aa1f-e94397e23952",
      "name": "Is Job Ready?",
      "type": "n8n-nodes-base.switch",
      "position": [
        -220,
        300
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "SUCCESS",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "300fce8c-b19a-4d0c-86e8-f62853c70ce2",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.status }}",
                    "rightValue": "SUCCESS"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "ERROR",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "e6058aa0-a3e2-4ce3-9bed-6ff41a5be052",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.status }}",
                    "rightValue": "ERROR"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "CANCELED",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "ceb6338f-4261-40ac-be11-91f61c7302ba",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.status }}",
                    "rightValue": "CANCELED"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "PENDING",
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "0fa97d86-432a-409a-917e-5f1a002b1ab9",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.status }}",
                    "rightValue": "PENDING"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "allMatchingOutputs": true
        }
      },
      "typeVersion": 3
    },
    {
      "id": "790f1b50-0cf0-4b6e-bf44-9f1ab81c2c53",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1480,
        20
      ],
      "parameters": {
        "color": 7,
        "width": 808.8727491350096,
        "height": 709.5781339256318,
        "content": "## 1. Watch for Invoice Emails\n[Read more about Gmail Triggers](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.gmailtrigger)\n\nThe Gmail node can watch for all incoming messages and filter based on a condition. We'll set our Gmail node to wait for:\n* a message from particular email address.\n* having an attachment which should be the invoice PDF\n* not having a label \"invoice synced\", which is what we use to avoid duplicate processing."
      },
      "typeVersion": 1
    },
    {
      "id": "07d74947-0253-433f-9cc5-a2d4df0b517a",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        180,
        -60
      ],
      "parameters": {
        "color": 7,
        "width": 805.0578351924228,
        "height": 656.5014186128178,
        "content": "## 3. Use LLMs to Extract Values from Data\n[Read more about Basic LLM Chain](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.chainllm/)\n\nLarge language models are perfect for data extraction tasks as they can work across a range of document layouts without human intervention. The extracted data can then be sent to a variety of datastores such as spreadsheets, accounting systems and/or CRMs.\n\n**Tip:** The \"Structured Output Parser\" ensures the AI output can be\ninserted to our spreadsheet without additional clean up and/or formatting. "
      },
      "typeVersion": 1
    },
    {
      "id": "dddc8d62-39a5-49db-8d61-b28427908e2e",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1020,
        40
      ],
      "parameters": {
        "color": 7,
        "width": 362.3535748101346,
        "height": 440.3435768155051,
        "content": "## 4. Add Label to Avoid Duplication\n[Read more about working with Gmail](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.gmail/)\n\nTo finish off the workflow, we'll add the \"invoice synced\" label to the original invoice email to flag that the extraction was successful. This can be useful if working with a shared inbox and for quality control purposes later."
      },
      "typeVersion": 1
    },
    {
      "id": "f42dd5ba-493d-446a-a830-4dc3706c25c8",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1880,
        20
      ],
      "parameters": {
        "width": 360.0244620907562,
        "height": 573.2443601155958,
        "content": "## Try Me Out!\n\n**This workflow does the following:**\n* Waits for email invoices with PDF attachments.\n* Uses the LlamaParse service to convert the invoice PDF into a markdown file.\n* Uses a LLM to extract invoice data from the Markdown file.\n* Exports the extracted data to a Google Sheet.\n\nHappy Hacking!"
      },
      "typeVersion": 1
    },
    {
      "id": "47acf270-8538-4472-ac1c-f7b1c8799513",
      "name": "Add \"invoice synced\" Label",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1140,
        280
      ],
      "parameters": {
        "labelIds": [
          "Label_7"
        ],
        "messageId": "={{ $('Receiving Invoices').item.json.id }}",
        "operation": "addLabels"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "8d48043d-77bb-4fd0-a70c-2ce17cbf60f2",
      "name": "Get Parsed Invoice Data",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -20,
        160
      ],
      "parameters": {
        "url": "=https://api.cloud.llamaindex.ai/api/parsing/job/{{ $json.id }}/result/markdown",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "50a6af24-76bc-47f9-98e7-e838acc4aaf1",
      "name": "Map Output",
      "type": "n8n-nodes-base.set",
      "position": [
        580,
        200
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={{ $json.output }}"
      },
      "typeVersion": 3.3
    },
    {
      "id": "159d5c04-04f6-4b92-8488-3db5b284a73c",
      "name": "Apply Data Extraction Rules",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        240,
        200
      ],
      "parameters": {
        "text": "=Given the following invoice in the <invoice> xml tags, extract the following information as listed below.\nIf you cannot find the information for a specific item, then leave blank and skip to the next. \n\n* PO No\n* PO date\n* PO release date\n* Payment terms\n* PO expiry date\n* Expected Delivery Date\n* Billing Address\n* Shipping Address\n* Customer VAT identification number\n* Shipping addresses\n* Line items, including a description of the goods or services rendered with with HSN Code, Quantity, MRP,CGST, IGST, Total \n* Price with and without Tax, \n* Total Tax\n* Grand Total\n\n<invoice>{{ $json.markdown }}</invoice>",
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.4
    },
    {
      "id": "c8eeb893-a290-49e1-91c0-b8070cfbb526",
      "name": "Should Process Email?",
      "type": "n8n-nodes-base.if",
      "position": [
        -840,
        280
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "e5649a2b-6e12-4cc4-8001-4639cc9cc2c2",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $input.item.binary.attachment_0.mimeType }}",
              "rightValue": "application/pdf"
            },
            {
              "id": "4c57ab9b-b11c-455a-a63d-daf48418b06e",
              "operator": {
                "type": "array",
                "operation": "notContains",
                "rightType": "any"
              },
              "leftValue": "={{ $json.labels }}",
              "rightValue": "invoice synced"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "5cc4330a-4ef9-4a69-b737-657dd08f971f",
      "name": "Split Out Labels",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -1200,
        280
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "labelIds"
      },
      "typeVersion": 1
    },
    {
      "id": "e6a8e39e-9189-4fec-95a2-90de7b58d9e6",
      "name": "Get Labels Names",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -1200,
        420
      ],
      "parameters": {
        "labelId": "={{ $json.labelIds }}",
        "resource": "label",
        "operation": "get"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "d889db2c-56ae-44a2-8f84-5ea21a7b39e8",
      "name": "Combine Label Names",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        -1200,
        560
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "renameField": true,
              "outputFieldName": "labels",
              "fieldToAggregate": "name"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "843fecd4-f964-42b6-b832-3a2d80101a08",
      "name": "Email with Label Names",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1020,
        280
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combinationMode": "multiplex"
      },
      "typeVersion": 2.1
    },
    {
      "id": "78f15e24-078a-433f-af0a-14dd35e7cac4",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        360,
        340
      ],
      "parameters": {
        "width": 192.26896179623753,
        "height": 213.73043662572252,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n**Need more attributes?**\nChange it here!"
      },
      "typeVersion": 1
    },
    {
      "id": "267837d1-5ad3-4b68-a7d2-c7ae201e1fa1",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        700,
        180
      ],
      "parameters": {
        "width": 258.29345180972877,
        "height": 397.0641952938746,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ud83d\udea8**Required**\n* Set Your Google Sheet URL here\n* Set the Name of your Sheet\n\n\n**Don't use GSheets?**\nSwap this for Excel, Airtable or a Database!"
      },
      "typeVersion": 1
    },
    {
      "id": "4a87dc87-b5f1-4aad-833c-6da1628a3dda",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1440,
        260
      ],
      "parameters": {
        "width": 174.50671517518518,
        "height": 274.6295678979021,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ud83d\udea8**Required**\n* Change the email filters here!"
      },
      "typeVersion": 1
    },
    {
      "id": "571ca78f-4d3f-4ee2-9f60-f461b9543277",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        240,
        380
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-2.5-flash-preview-05-20"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Map Output": {
      "main": [
        [
          {
            "node": "Append to Reconciliation Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Job Ready?": {
      "main": [
        [
          {
            "node": "Get Parsed Invoice Data",
            "type": "main",
            "index": 0
          }
        ],
        [],
        [],
        [
          {
            "node": "Wait to stay within service limits",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Labels Names": {
      "main": [
        [
          {
            "node": "Combine Label Names",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out Labels": {
      "main": [
        [
          {
            "node": "Get Labels Names",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receiving Invoices": {
      "main": [
        [
          {
            "node": "Split Out Labels",
            "type": "main",
            "index": 0
          },
          {
            "node": "Email with Label Names",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Label Names": {
      "main": [
        [
          {
            "node": "Email with Label Names",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Upload to LlamaParse": {
      "main": [
        [
          {
            "node": "Get Processing Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Processing Status": {
      "main": [
        [
          {
            "node": "Is Job Ready?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Should Process Email?": {
      "main": [
        [
          {
            "node": "Upload to LlamaParse",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email with Label Names": {
      "main": [
        [
          {
            "node": "Should Process Email?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Parsed Invoice Data": {
      "main": [
        [
          {
            "node": "Apply Data Extraction Rules",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Apply Data Extraction Rules",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Apply Data Extraction Rules",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Apply Data Extraction Rules": {
      "main": [
        [
          {
            "node": "Map Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append to Reconciliation Sheet": {
      "main": [
        [
          {
            "node": "Add \"invoice synced\" Label",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait to stay within service limits": {
      "main": [
        [
          {
            "node": "Get Processing Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}