AutomationFlowsAI & RAG › Extract Invoice Data From Google Drive Images with Gpt-4o and Postgres

Extract Invoice Data From Google Drive Images with Gpt-4o and Postgres

Bypawan @pawansah on n8n.io

This workflow watches a Google Drive folder for new invoice images, extracts key invoice fields with OpenAI vision, checks for duplicates in Postgres, and then stores the invoice or moves the file into processed, duplicate, or failed folders. Triggers every hour when a new file…

Event trigger★★★★☆ complexityAI-powered16 nodesGoogle Drive TriggerOpenAIPostgresGoogle Drive
AI & RAG Trigger: Event Nodes: 16 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #17023 — we link there as the canonical source.

This workflow follows the Google Drive → Google Drive Trigger recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "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
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow watches a Google Drive folder for new invoice images, extracts key invoice fields with OpenAI vision, checks for duplicates in Postgres, and then stores the invoice or moves the file into processed, duplicate, or failed folders. Triggers every hour when a new file…

Source: https://n8n.io/workflows/17023/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Use n8n to extract medical test data from diagnostic reports uploaded to Google Drive, automatically detect abnormal values, and generate personalized health advice. Upload a medical report (PDF or im

Google Drive, Google Drive Trigger, Mistral Ai +2
AI & RAG

Social Media Audio Extractor. Uses telegramTrigger, telegram, openAi, httpRequest. Event-driven trigger; 31 nodes.

Telegram Trigger, Telegram, OpenAI +2
AI & RAG

Baby Chaganti. Uses httpRequest, googleDrive, youTube, openAi. Event-driven trigger; 23 nodes.

HTTP Request, Google Drive, YouTube +1
AI & RAG

This workflow monitors a Google Drive folder for newly uploaded invoice files, extracts text with OCR.Space, uses Google Gemini to convert the OCR text into structured invoice fields, validates requir

Google Drive Trigger, Google Drive, HTTP Request +3
AI & RAG

Transform your receipt management with this comprehensive n8n workflow that automatically processes receipts through Telegram, extracts transaction data using AI, and stores it across multiple platfor

Telegram Trigger, Telegram, HTTP Request +4