{
  "id": "YV6md_-Qx314udyFD-gkv",
  "name": "My workflow 2",
  "tags": [],
  "nodes": [
    {
      "id": "324fb5c9-184f-475f-847d-bc3996df1068",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        -192
      ],
      "parameters": {
        "width": 480,
        "height": 832,
        "content": "## Invoice processing workflow\n\n### How it works\n\n1. Triggers when a file is added to Google Drive.\n2. Downloads the file from Google Drive.\n3. Analyzes the image using OpenAI.\n4. Parses analysis results in JSON format.\n5. Checks if the result is a duplicate and processes accordingly.\n\n### Setup steps\n\n- [ ] Configure Google Drive credentials for access.\n- [ ] Set up OpenAI credentials for image analysis.\n- [ ] Configure database credentials for PostgreSQL.\n- [ ] Set up Google Drive folders for failed, processed, and duplicate files.\n\n### Customization\n\nThe workflow can be customized to handle different types of files or use different AI models for analysis."
      },
      "typeVersion": 1
    },
    {
      "id": "c4afaecf-283d-4cd8-b618-31d4e5542011",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        336,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 304,
        "content": "## Google Drive file trigger\n\nTriggers on new files in Google Drive and downloads them."
      },
      "typeVersion": 1
    },
    {
      "id": "6cb2b0ee-2c1e-4376-b0d7-47f205f11ae9",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        784,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 336,
        "content": "## Analyze and parse image data\n\nAnalyzes image with OpenAI and parses results."
      },
      "typeVersion": 1
    },
    {
      "id": "cdcca1d4-6120-4d42-ac23-b399cb3e6e25",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1232,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 304,
        "content": "## Check for duplicates\n\nChecks database for duplicates and branches workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "806b7402-a5e9-4e55-8825-c05017c697bb",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1680,
        -192
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 496,
        "content": "## Process results to database\n\nInserts data into database and moves files based on status."
      },
      "typeVersion": 1
    },
    {
      "id": "ab8e8faf-0db2-4601-8261-a75dd5bbe9bb",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1904,
        336
      ],
      "parameters": {
        "color": 7,
        "height": 304,
        "content": "## Error handling\n\nHandles errors and moves failed operations."
      },
      "typeVersion": 1
    },
    {
      "id": "e7ec7077-4524-43d7-b0df-44e980831b26",
      "name": "Google Drive File Trigger",
      "type": "n8n-nodes-base.googleDriveTrigger",
      "position": [
        384,
        176
      ],
      "parameters": {
        "event": "fileCreated",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyHour"
            }
          ]
        },
        "triggerOn": "specificFolder",
        "folderToWatch": {
          "__rl": true,
          "mode": "list",
          "value": "16PIFdQOW_Thxuq4sOv5a3bgAhz_yC1Z8",
          "cachedResultUrl": "https://drive.google.com/drive/folders/16PIFdQOW_Thxuq4sOv5a3bgAhz_yC1Z8",
          "cachedResultName": "incoming"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "76239e58-f470-4fc5-9636-5a22af44ba43",
      "name": "AI Image Analyzer",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "onError": "continueErrorOutput",
      "position": [
        832,
        176
      ],
      "parameters": {
        "text": "Extract all invoice information from this image.\n\nReturn ONLY valid JSON.\n\n{\n  \"vendor_name\": \"\",\n  \"invoice_number\": \"\",\n  \"invoice_date\": \"\",\n  \"due_date\": \"\",\n  \"currency\": \"\",\n  \"subtotal\": 0,\n  \"tax\": 0,\n  \"total\": 0,\n  \"po_number\": \"\"\n}\n\nRules:\n- Do not explain.\n- Use null if the value is unavailable.\n- Dates format YYYY-MM-DD.\n- Amounts must be numeric.",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "GPT-4O"
        },
        "options": {},
        "resource": "image",
        "inputType": "base64",
        "operation": "analyze"
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1,
      "alwaysOutputData": false
    },
    {
      "id": "c3a0c82f-6ade-4477-affd-b7db3fb434d2",
      "name": "Parse JSON Response",
      "type": "n8n-nodes-base.code",
      "onError": "continueErrorOutput",
      "position": [
        1056,
        112
      ],
      "parameters": {
        "jsCode": "const output = [];\n\nfor (const item of $input.all()) {\n  try {\n    let text = item.json[0].content[0].text;\n\n    text = text\n      .replace(/```json/g, '')\n      .replace(/```/g, '')\n      .trim();\n\n    const parsed = JSON.parse(text);\n\n    output.push({\n      json: {\n        vendor_name: parsed.vendor_name,\n        invoice_number: parsed.invoice_number,\n        invoice_date: parsed.invoice_date,\n        due_date: parsed.due_date,\n        currency: parsed.currency,\n        subtotal: parsed.subtotal,\n        tax: parsed.tax,\n        total: parsed.total,\n        po_number: parsed.po_number\n      }\n    });\n\n  } catch (err) {\n    console.log('Skipping invalid response');\n  }\n}\n\nreturn output;"
      },
      "typeVersion": 2
    },
    {
      "id": "b9a1e089-0d39-4710-ae42-8675308b7fa4",
      "name": "Check Duplicate Entry",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1280,
        32
      ],
      "parameters": {
        "query": "SELECT EXISTS (\n  SELECT 1\n  FROM invoices\n  WHERE vendor_name = $1\n    AND invoice_number = $2\n    AND total = $3\n) AS is_duplicate;",
        "options": {
          "queryReplacement": "=$1 = {{$json.vendor_name}} $2 = {{$json.invoice_number}} $3 = {{$json.total}}"
        },
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6,
      "alwaysOutputData": false
    },
    {
      "id": "62f491a4-181d-4393-a5b9-0a52ced8a6d2",
      "name": "Insert Invoice Record",
      "type": "n8n-nodes-base.postgres",
      "onError": "continueErrorOutput",
      "position": [
        1728,
        128
      ],
      "parameters": {
        "query": "INSERT INTO invoices (\n  vendor_name,\n  invoice_number,\n  invoice_date,\n  due_date,\n  currency,\n  subtotal,\n  tax,\n  total,\n  po_number,\n  status\n)\nVALUES (\n  $1, $2, $3, $4, $5,\n  $6, $7, $8, $9, $10\n)\nRETURNING *;",
        "options": {
          "queryReplacement": "=$1  = {{$node[\"JSON Parser\"].json[\"vendor_name\"]}}\n$2  = {{$node[\"JSON Parser\"].json[\"invoice_number\"]}}\n$3  = {{$node[\"JSON Parser\"].json[\"invoice_date\"]}}\n$4  = {{$node[\"JSON Parser\"].json[\"due_date\"]}}\n$5  = {{$node[\"JSON Parser\"].json[\"currency\"]}}\n$6  = {{$node[\"JSON Parser\"].json[\"subtotal\"]}}\n$7  = {{$node[\"JSON Parser\"].json[\"tax\"]}}\n$8  = {{$node[\"JSON Parser\"].json[\"total\"]}}\n$9  = {{$node[\"JSON Parser\"].json[\"po_number\"]}}\n$10 = {{\"processed\"}}"
        },
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "9387d745-4f29-4644-a0b9-4437d304d9f0",
      "name": "Download from Google Drive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        608,
        176
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.id }}"
        },
        "options": {},
        "operation": "download"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "aaebbf41-b5e7-49e8-8e67-8a8c668e97c9",
      "name": "Move to Failed Folder",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1952,
        464
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $node[\"Google Drive Trigger\"].json[\"id\"] }}"
        },
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive",
          "cachedResultUrl": "https://drive.google.com/drive/my-drive",
          "cachedResultName": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "1iTgZpC4l3hArO0dqeE2-2pIgXPfy5lTJ",
          "cachedResultUrl": "https://drive.google.com/drive/folders/1iTgZpC4l3hArO0dqeE2-2pIgXPfy5lTJ",
          "cachedResultName": "failed"
        },
        "operation": "move"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "70be5983-ae21-44b1-828c-42350d640db1",
      "name": "Move to Processed Folder",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1952,
        80
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $node[\"Google Drive Trigger\"].json[\"id\"] }}"
        },
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive",
          "cachedResultUrl": "https://drive.google.com/drive/my-drive",
          "cachedResultName": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "1fMqBIvtbHgTsTSkgJeM9jrh5CchO-KVk",
          "cachedResultUrl": "https://drive.google.com/drive/folders/1fMqBIvtbHgTsTSkgJeM9jrh5CchO-KVk",
          "cachedResultName": "processed"
        },
        "operation": "move"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "d7a51f2c-b270-4161-a0a1-67274ffaa727",
      "name": "If Duplicate Found",
      "type": "n8n-nodes-base.if",
      "position": [
        1504,
        32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1189a7e2-3fbe-498e-b1d4-328cc11b4f44",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.is_duplicate }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.3,
      "alwaysOutputData": false
    },
    {
      "id": "f144aed6-1e20-4f0f-87b1-5d527f21257c",
      "name": "Move to Duplicate Folder",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1728,
        -64
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $node[\"Google Drive Trigger\"].json[\"id\"] }}"
        },
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive",
          "cachedResultUrl": "https://drive.google.com/drive/my-drive",
          "cachedResultName": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "1VBt3EFubENS2C5FdbgRzRH7frHF0Aw2g",
          "cachedResultUrl": "https://drive.google.com/drive/folders/1VBt3EFubENS2C5FdbgRzRH7frHF0Aw2g",
          "cachedResultName": "duplicate"
        },
        "operation": "move"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "e0ab9898-0825-4ef2-bb39-3a27d6b36461",
  "connections": {
    "AI Image Analyzer": {
      "main": [
        [
          {
            "node": "Parse JSON Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Move to Failed Folder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Duplicate Found": {
      "main": [
        [
          {
            "node": "Move to Duplicate Folder",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Insert Invoice Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse JSON Response": {
      "main": [
        [
          {
            "node": "Check Duplicate Entry",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Move to Failed Folder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Duplicate Entry": {
      "main": [
        [
          {
            "node": "If Duplicate Found",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert Invoice Record": {
      "main": [
        [
          {
            "node": "Move to Processed Folder",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Move to Failed Folder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Drive File Trigger": {
      "main": [
        [
          {
            "node": "Download from Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download from Google Drive": {
      "main": [
        [
          {
            "node": "AI Image Analyzer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}