{
  "id": "gQhYUHmv0KdIsS6n",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Analytics",
  "tags": [],
  "nodes": [
    {
      "id": "67a02659-4fe1-4394-89e8-03c4819dc1ff",
      "name": "Email Trigger (IMAP)",
      "type": "n8n-nodes-base.emailReadImap",
      "disabled": true,
      "position": [
        -3552,
        -144
      ],
      "parameters": {
        "options": {},
        "downloadAttachments": true
      },
      "credentials": {
        "imap": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "c3ebd896-96af-4c81-a916-aa19df57c8d3",
      "name": "Discord",
      "type": "n8n-nodes-base.discord",
      "position": [
        -832,
        -208
      ],
      "parameters": {
        "files": {
          "values": [
            {}
          ]
        },
        "content": "=# :rotating_light: New Invoice: {{ $json.output.invoiceDetails.invoiceNumber }}\nBuyer: {{ $json.output.invoiceDetails.recipient.name }}\n            Address: {{ $json.output.invoiceDetails.recipient.address }}\n            TAX ID:  {{ $json.output.invoiceDetails.recipient.taxNumber }}\n\nSeller: {{ $json.output.invoiceDetails.issuer.name }}\n            Address: {{ $json.output.invoiceDetails.issuer.address }}\n            TAX ID:  {{ $json.output.invoiceDetails.issuer.taxNumber }}\n\nBank: {{ $json.output.invoiceDetails.bankAccountDetails.bankName }}\nAccount Number: {{ $json.output.invoiceDetails.bankAccountDetails.accountNumber }}\n\nPayment Due Date: {{ $json.output.invoiceDetails.paymentDueDate }}\n:dollar: Total Due: {{ $json.output.invoiceDetails.totalToPay }} {{ $json.output.invoiceDetails.currency }} ",
        "options": {},
        "authentication": "webhook"
      },
      "credentials": {
        "discordWebhookApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "e3754a1f-ea3b-4027-91ee-0d96c38270f2",
      "name": "Basic LLM Chain",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -2144,
        -272
      ],
      "parameters": {
        "text": "Determine whether the provided text is an invoice. If it is, extract the following details: the invoice number, the items listed on the invoice along with their prices, the recipient and the issuer, the payment due date in the format dd-MM-yyyy, bank account details, the total net and gross amounts, the total amount to pay, and the currency of the invoice",
        "messages": {
          "messageValues": [
            {
              "type": "HumanMessagePromptTemplate",
              "message": "= Filename: {{ $binary.data.fileName }}  Data: {{ $json.text }}"
            }
          ]
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.5
    },
    {
      "id": "0e75a30b-b631-45c1-a957-71e8a20c9f9b",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -2192,
        64
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c9555958-80ce-40d9-81f7-095f5fd59225",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -1920,
        64
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"fileName\": \"test.pdf\",\n  \"isInvoice\": true,\n  \"invoiceDetails\": {\n    \"invoiceNumber\": \"INV-2025-00123\",\n    \"issuer\": {\n      \"name\": \"ABC Company\",\n      \"address\": \"123 Business St, Warsaw, Poland\",\n      \"taxNumber\": \"547-896-544-52\"\n    },\n    \"recipient\": {\n      \"name\": \"XYZ Ltd.\",\n      \"address\": \"456 Client Rd, Krakow, Poland\",\n      \"taxNumber\": \"54789654452\"\n    },\n    \"items\": [\n      {\n        \"description\": \"Consulting Services\",\n        \"quantity\": 10,\n        \"unitPrice\": 100.00,\n        \"totalPrice\": 1000.00\n      }\n    ],\n    \"totalNet\": 1500.00,\n    \"totalGross\": 1845.00,\n    \"totalToPay\": 1845.00,\n    \"currency\": \"PLN\",\n    \"paymentDueDate\": \"2025-03-15\",\n    \"bankAccountDetails\": {\n      \"bankName\": \"Example Bank\",\n      \"accountNumber\": \"PL6110901014+1234567890\"\n    }\n  }\n}"
      },
      "typeVersion": 1.2
    },
    {
      "id": "9444f054-ab31-4723-ad9b-43907f71d81a",
      "name": "Code",
      "type": "n8n-nodes-base.code",
      "position": [
        -2928,
        -160
      ],
      "parameters": {
        "jsCode": "const newItems = [];\n\nfor (const key of Object.keys(items[0].binary)) {\n  const binaryData = items[0].binary[key];\n\n  // Check if the file is a PDF\n  if (binaryData.mimeType === \"application/pdf\") {\n    newItems.push({\n      json: { fileName: key },\n      binary: {\n        data: binaryData\n      }\n    });\n  }\n}\n\nreturn newItems;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "42bf86b9-14f3-4ba1-b0f8-60ae8650e5f0",
      "name": "Extract from File1",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -2464,
        -176
      ],
      "parameters": {
        "options": {
          "keepSource": "binary"
        },
        "operation": "pdf"
      },
      "typeVersion": 1
    },
    {
      "id": "eeefc5f4-3c7f-49d7-8b21-3a98462c702e",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        -3216,
        -144
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "09333af2-d5b3-4525-8f30-568b666263d9",
              "operator": {
                "type": "object",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $binary }}",
              "rightValue": ""
            },
            {
              "id": "a9cdfc35-c960-49dc-aad6-861814cee8ff",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f7a051ed-f99a-4a5a-b6b7-b32f8e2f5fdf",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1584,
        -192
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3
    },
    {
      "id": "7e57500b-1a8f-4854-8221-7edc2336a9dd",
      "name": "If1",
      "type": "n8n-nodes-base.if",
      "position": [
        -2704,
        -160
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "636c5c8b-abf2-4da0-84e0-a11aad1daee4",
              "operator": {
                "type": "object",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $binary }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "250cf1ac-90bc-4868-a8c6-7898c393d790",
      "name": "If2",
      "type": "n8n-nodes-base.if",
      "position": [
        -1296,
        -192
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c75b9dda-ce11-41f1-8bcc-48b32406c4f4",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.output.isInvoice }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "3406be94-e2e0-4a3d-af3d-d6c5c8cd1279",
      "name": "Prepare Company Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -1280,
        192
      ],
      "parameters": {
        "values": {
          "string": [
            {
              "name": "issuer_tax_number",
              "value": "={{ $json.invoiceDetails.issuer.taxNumber }}"
            },
            {
              "name": "issuer_name",
              "value": "={{ $json.invoiceDetails.issuer.name }}"
            },
            {
              "name": "issuer_address",
              "value": "={{ $json.invoiceDetails.issuer.address }}"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "7b540304-d0aa-40c9-b40b-6c6dc15ea557",
      "name": "Check Company",
      "type": "n8n-nodes-base.postgres",
      "position": [
        -1072,
        192
      ],
      "parameters": {
        "query": "=SELECT id FROM company WHERE tax_number = '{{ $json.issuer_tax_number }}';",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "ee7ca336-4f6b-4f2e-94e6-7526896e5c52",
      "name": "IF Company Does Not Exist",
      "type": "n8n-nodes-base.if",
      "position": [
        -880,
        192
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $items('Check Company')[0].json.length }}"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "180d3810-a537-447b-b6db-78fe01daebe7",
      "name": "Add Company",
      "type": "n8n-nodes-base.postgres",
      "position": [
        -672,
        80
      ],
      "parameters": {
        "table": "company",
        "schema": "public",
        "options": {}
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "e91420fb-0b15-4067-99e9-bf1f5c493bc6",
      "name": "Set Existing Company ID",
      "type": "n8n-nodes-base.set",
      "position": [
        -576,
        288
      ],
      "parameters": {
        "values": {
          "number": [
            {
              "name": "company_id",
              "value": "={{ $items('Check Company')[0].json[0].id }}"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "4dfdc187-084e-4500-9249-fa2d0df5064c",
      "name": "Set New Company ID",
      "type": "n8n-nodes-base.set",
      "position": [
        -480,
        80
      ],
      "parameters": {
        "values": {
          "number": [
            {
              "name": "company_id",
              "value": "={{ $json.id }}"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "a854c3c2-3375-4f80-84e1-fc327c30dce8",
      "name": "Merge Company ID",
      "type": "n8n-nodes-base.merge",
      "position": [
        -272,
        192
      ],
      "parameters": {},
      "typeVersion": 2
    },
    {
      "id": "76edc407-fac3-43f2-902e-c8e3de3031c8",
      "name": "Check Invoice",
      "type": "n8n-nodes-base.postgres",
      "position": [
        -80,
        192
      ],
      "parameters": {
        "query": "=SELECT id FROM invoice WHERE invoice_number = '{{ $json.invoiceDetails.invoiceNumber }}' AND company_id = {{ $json.company_id }};",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "e82706a5-0415-49d1-aa10-280d8b0ed9a9",
      "name": "IF Invoice Does Not Exist",
      "type": "n8n-nodes-base.if",
      "position": [
        144,
        192
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $items('Check Invoice')[0].json.length }}",
              "value2": 0
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "cf030538-7ab2-4199-979e-82fabc83a8a6",
      "name": "Add Invoice",
      "type": "n8n-nodes-base.postgres",
      "position": [
        416,
        80
      ],
      "parameters": {
        "table": "invoice",
        "schema": "public",
        "options": {}
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "a25f62a0-4334-46aa-8ad7-fbcfb59b8910",
      "name": "Invoice Exists",
      "type": "n8n-nodes-base.set",
      "position": [
        416,
        288
      ],
      "parameters": {
        "values": {
          "string": [
            {
              "name": "status",
              "value": "Invoice already exists"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "5e8aa135-8ee3-4ced-b239-f35aeb5eacbb",
      "name": "Merge End",
      "type": "n8n-nodes-base.merge",
      "position": [
        752,
        192
      ],
      "parameters": {},
      "typeVersion": 2
    },
    {
      "id": "d4dd9cd0-2ef9-4038-9cb8-8779edbf1887",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3712,
        -560
      ],
      "parameters": {
        "color": 6,
        "width": 432,
        "height": 672,
        "content": "### \ud83d\udce5 Email Trigger (IMAP)\n\nThis node listens for new emails in your inbox and fetches PDF attachments.\n\n**Setup Instructions:**\n- Go to **Credentials** and create new IMAP credentials  \n- Fill in: IMAP host, port, email address, and password  \n- Optionally filter emails (e.g. only those with PDF attachments)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "33a8dc7b-e181-495d-bfb3-f352ff883c17",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2256,
        -576
      ],
      "parameters": {
        "color": 6,
        "width": 816,
        "height": 800,
        "content": "### \ud83e\udd16 AI Data Extraction (Basic LLM Chain)\n\nThis node sends the text from the PDF to an AI model (e.g. GPT-4o) to extract structured invoice data.\n\n**Setup Instructions:**\n- Provide a clear prompt describing which data to extract  \n- Example: invoice number, dates, seller, buyer, tax number, total, currency  \n- Use JSON as the expected response format\n\n**Note:** \nIf the model fails to read some invoices (e.g. unusual layout, languages, or formats), try improving or customizing the prompt.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "bacc660a-5fd2-463b-ba2e-54cb94a1bdde",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2256,
        256
      ],
      "parameters": {
        "color": 3,
        "width": 432,
        "height": 304,
        "content": "### \ud83d\udd11 OpenAI API (LLM Provider)\n\nThis node connects to OpenAI (or another AI provider) and sends the invoice content for analysis.\n\n**Setup Instructions:**\n- Log in to [OpenAI](https://platform.openai.com) or your provider  \n- Generate an API key  \n- In **n8n**, create new credentials for the OpenAI node  \n- Select the correct model (`gpt-4o`, `gpt-3.5-turbo`, etc.)\n\n**Tip:** You can replace OpenAI with Anthropic, Mistral, or any compatible LLM node.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "970d4c31-8c78-482f-b10b-e3350bea6634",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1056,
        -576
      ],
      "parameters": {
        "color": 6,
        "width": 560,
        "height": 544,
        "content": "### \ud83d\udce4 Chat Notification (Discord Webhook)\n\nThis node sends a summary of the extracted invoice to your Discord channel.\n\n**Setup Instructions:**\n- In Discord:  \n  Go to Server Settings \u2192 Integrations \u2192 Webhooks \u2192 Create Webhook  \n- Choose the target channel and copy the webhook URL  \n- Paste the URL into this node in n8n\n\n**Alternative:** \nSwap with Slack, Telegram, MS Teams, or another notification service.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "7f9798aa-bc2c-4653-9521-d2530e3917d9",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -368,
        384
      ],
      "parameters": {
        "color": 3,
        "width": 704,
        "height": 304,
        "content": "### \u2757 Don't forget to set your PostgreSQL credentials\n\nIn every PostgreSQL-related node, you must manually select your own credentials from the dropdown.\n\n**Instructions:**\n- Click on the node  \n- Go to the **\"Credentials\"** section  \n- Select your custom PostgreSQL connection (e.g. `My Production DB`)  \n- Make sure the credentials have access to the correct database and tables\n\n\u26a0\ufe0f The workflow will fail if no credentials are selected or if the connection is invalid.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "27a3576a-2946-41ee-98af-be1e68a1011a",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        544,
        384
      ],
      "parameters": {
        "color": 6,
        "width": 544,
        "height": 320,
        "content": "### \ud83d\udd00 Merge Node\n\nThis node merges metadata from the email (e.g., subject, sender) with the AI-extracted invoice content.\n\n**Setup Instructions:**\n- Input 1 \u2192 AI output  \n- Input 2 \u2192 Email data  \n- Set **Merge Strategy** to \u201cMerge By Index\u201d (if both inputs are aligned)\n\n**Use case:** \nAttach additional metadata (e.g., sender email) to your final record or notification.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ccb75df0-4db9-40e1-ab87-11914e81d36f",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1328,
        384
      ],
      "parameters": {
        "color": 6,
        "width": 624,
        "height": 512,
        "content": "### \ud83d\uddc3\ufe0f PostgreSQL Insert\n\nThis node writes the extracted data into the `company` and `invoice` tables in your PostgreSQL database.\n\n**Setup Instructions:**\n- Ensure your database has the correct schema (see SQL setup)  \n- Create credentials in **n8n** for your database  \n- Map each extracted field to the corresponding column  \n- Use \u201cInsert\u201d or \u201cInsert & Update\u201d depending on your logic\n\n**Alternative:** \nYou can replace this with MySQL, Airtable, Supabase, or Google Sheets.\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "5729a984-3945-4991-a780-0077ab8f919f",
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If1": {
      "main": [
        [
          {
            "node": "Extract from File1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If2": {
      "main": [
        [
          {
            "node": "Discord",
            "type": "main",
            "index": 0
          },
          {
            "node": "Prepare Company Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code": {
      "main": [
        [
          {
            "node": "If1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "If2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Company": {
      "main": [
        [
          {
            "node": "Set New Company ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Invoice": {
      "main": [
        [
          {
            "node": "Merge End",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Company": {
      "main": [
        [
          {
            "node": "IF Company Does Not Exist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Invoice": {
      "main": [
        [
          {
            "node": "IF Invoice Does Not Exist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Invoice Exists": {
      "main": [
        [
          {
            "node": "Merge End",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Basic LLM Chain": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Company ID": {
      "main": [
        [
          {
            "node": "Check Invoice",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Basic LLM Chain",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File1": {
      "main": [
        [
          {
            "node": "Basic LLM Chain",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Set New Company ID": {
      "main": [
        [
          {
            "node": "Merge Company ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email Trigger (IMAP)": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Company Data": {
      "main": [
        [
          {
            "node": "Check Company",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Existing Company ID": {
      "main": [
        [
          {
            "node": "Merge Company ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Basic LLM Chain",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "IF Company Does Not Exist": {
      "main": [
        [
          {
            "node": "Add Company",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set Existing Company ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Invoice Does Not Exist": {
      "main": [
        [
          {
            "node": "Add Invoice",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Invoice Exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}