AutomationFlowsEmail & Gmail › Extract Invoices From Gmail with Laiye Adp and Save Them to Google Drive

Extract Invoices From Gmail with Laiye Adp and Save Them to Google Drive

ByLaiye-ADP @laiye-adp on n8n.io

Our invoice extraction workflow is completed end-to-end automatically: Gmail invoice email screening → extraction of key fields from multi-format invoices → automatic archiving of results to Google Drive, replacing the repetitive manual labor of finance staff in opening and…

Event trigger★★★★☆ complexity15 nodesGmail TriggerGoogle DriveHTTP Request
Email & Gmail Trigger: Event Nodes: 15 Complexity: ★★★★☆ Added:

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

This workflow follows the Gmail Trigger → Google Drive 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": "AOioHpxTTmZ5y5gC",
  "name": "From Gmail to Google Drive, Laiye ADP fully automated invoice extraction",
  "tags": [],
  "nodes": [
    {
      "id": "0d9d5f81-fdd0-4ce3-a76b-63d5ff0115b7",
      "name": "Gmail Trigger",
      "type": "n8n-nodes-base.gmailTrigger",
      "position": [
        896,
        480
      ],
      "parameters": {
        "simple": false,
        "filters": {
          "labelIds": [
            "INBOX"
          ],
          "readStatus": "both"
        },
        "options": {
          "downloadAttachments": true,
          "dataPropertyAttachmentsPrefixName": "attachment_"
        },
        "pollTimes": {
          "item": [
            {
              "hour": 23,
              "minute": 59
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "aaec31d1-c60a-4fde-bbf7-6a9bac5e09da",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        2192,
        544
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "9960d10f-3231-4bbb-8265-9a051cced530",
      "name": "Upload Unprocessed file",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        2384,
        544
      ],
      "parameters": {
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.id }}"
        },
        "inputDataFieldName": "=file"
      },
      "retryOnFail": false,
      "typeVersion": 3
    },
    {
      "id": "6035c47b-6f5d-4d47-88bf-ad908cb55adf",
      "name": "Extract attachment information",
      "type": "n8n-nodes-base.code",
      "position": [
        1232,
        480
      ],
      "parameters": {
        "jsCode": "const outputItems = [];\n\n$input.all().forEach((inputItem) => {\n  const { json, binary } = inputItem;\n\n  // Iterate all binary attachments (attachment_0, attachment_1...)\n  const binaryKeys = Object.keys(binary || {});\n\n  if (binaryKeys.length === 0) {\n    // Return empty metadata when no attachments to avoid process interruption\n    outputItems.push({\n      json: {\n        \"attachment_name\": \"\",\n        \"attachment_extension\": \"\",\n        \"mime_type\": \"\",\n        \"attachment_size_bytes\": 0,\n        \"attachment_size_mb\": 0,\n        \"has_attachment\": false,\n        \"email_id\": json.id || \"\"\n      },\n      binary: {}\n    });\n    return;\n  }\n\n  // Process each attachment when available\n  binaryKeys.forEach((binaryKey) => {\n    const att = binary[binaryKey];\n    if (!att) return;\n\n    // Extract metadata from binary\n    const fileName = att.fileName || \"\";\n    const fileExt = att.fileExtension || \"\";\n    const mimeType = att.mimeType || \"\";\n    const fileSizeStr = att.fileSize || \"0 kB\";\n\n    // Parse file size to bytes\n    let fileSizeBytes = 0;\n    const sizeMatch = fileSizeStr.match(/(\\d+(?:\\.\\d+)?)\\s*(kB|MB|GB|B)/i);\n    if (sizeMatch) {\n      const value = parseFloat(sizeMatch[1]);\n      const unit = sizeMatch[2].toUpperCase();\n      switch (unit) {\n        case \"B\":\n          fileSizeBytes = value;\n          break;\n        case \"KB\":\n          fileSizeBytes = value * 1024;\n          break;\n        case \"MB\":\n          fileSizeBytes = value * 1024 * 1024;\n          break;\n        case \"GB\":\n          fileSizeBytes = value * 1024 * 1024 * 1024;\n          break;\n      }\n    }\n\n    const fileSizeMB = (fileSizeBytes / 1024 / 1024).toFixed(2);\n\n    outputItems.push({\n      json: {\n        \"attachment_name\": fileName,\n        \"attachment_extension\": fileExt,\n        \"mime_type\": mimeType,\n        \"attachment_size_bytes\": Math.round(fileSizeBytes),\n        \"attachment_size_mb\": parseFloat(fileSizeMB),\n        \"has_attachment\": true,\n        \"email_id\": json.id || \"\",\n        \"binary_key\": binaryKey // Used to associate binary files later\n      },\n      // Core modification: Rename all attachments to \"file\" instead of attachment_X\n      binary: {\n        file: att\n      }\n    });\n  });\n});\n\nreturn outputItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "c898a939-f009-414a-a57a-0cf935901aa7",
      "name": "Filter documents",
      "type": "n8n-nodes-base.if",
      "position": [
        1552,
        480
      ],
      "parameters": {
        "options": {
          "ignoreCase": true
        },
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1d3c412e-2393-4c9f-9505-031b0a9b9f4c",
              "operator": {
                "type": "string",
                "operation": "regex"
              },
              "leftValue": "={{ $json.attachment_name }}",
              "rightValue": "={{ \"invoice|receipt|expenses|fee\" }}"
            },
            {
              "id": "b51b0700-dc6c-4342-b67d-f0c060e3e526",
              "operator": {
                "type": "string",
                "operation": "regex"
              },
              "leftValue": "={{ $json.attachment_extension }}",
              "rightValue": "={{ \"^(jpeg|jpg|png|bmp|tiff|pdf|doc|docx|xls|xlsx)$\" }}"
            },
            {
              "id": "16f33e86-b523-4fa8-b1be-3cc4ad5a78df",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $json.attachment_size_mb }}",
              "rightValue": "={{ 50 }}"
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "7ed141be-6fd4-4729-b700-0d28039273bc",
      "name": "Base64 Encode Document",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        1920,
        288
      ],
      "parameters": {
        "options": {
          "encoding": "base64"
        },
        "operation": "binaryToPropery",
        "binaryPropertyName": "=file"
      },
      "typeVersion": 1.1
    },
    {
      "id": "fff5863f-fe64-4458-91df-25dfc423bf30",
      "name": "Laiye ADP HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2160,
        288
      ],
      "parameters": {
        "url": "https://adp.laiye.com/open/agentic_doc_processor/laiye/v1/app/doc/extract",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"app_key\": \"enter_your_app_key\",\n  \"app_secret\":\"enter_your_app_secret\",\n  \"file_base64\": \"{{ $json.data }}\"\n}",
        "sendBody": true,
        "jsonHeaders": "={\n  \"X-Access-Key\": \"enter_your_access_key\",\n  \"X-Timestamp\": \"{{ $now.valueOf() }}\",\n  \"X-Signature\": \"enter_your_access_key\"\n}",
        "sendHeaders": true,
        "specifyBody": "json",
        "specifyHeaders": "json"
      },
      "typeVersion": 4.3
    },
    {
      "id": "b45702da-c2c7-411b-a9b6-adeb511b37f4",
      "name": "Result Processor",
      "type": "n8n-nodes-base.code",
      "position": [
        2368,
        288
      ],
      "parameters": {
        "jsCode": "  var data = $node['Laiye ADP HTTP Request'].json.data.extraction_result;                                                                                            var mainFields = {};\n  var detailHeaders = [];                                                                                                                                \n  var detailRows = [];\n\n  for (var i = 0; i < data.length; i++) {\n    if (data[i].table_values == null) {\n      var name = data[i].field_name;\n      var val = 'No Result';\n      if (data[i].field_values && data[i].field_values[0]) {\n        val = data[i].field_values[0].field_value || 'No Result';\n      }\n      mainFields[name] = val;\n    } else {\n      for (var j = 0; j < data[i].table_values[0].length; j++) {\n        detailHeaders.push(data[i].table_values[0][j].field_name);\n      }\n      for (var k = 0; k < data[i].table_values.length; k++) {\n        var row = [];\n        for (var m = 0; m < data[i].table_values[k].length; m++) {\n          var cell = 'No Result';\n          if (data[i].table_values[k][m].field_values && data[i].table_values[k][m].field_values[0]) {\n            cell = data[i].table_values[k][m].field_values[0].field_value || 'No Result';\n          }\n          row.push(cell);\n        }\n        detailRows.push(row);\n      }\n    }\n  }\n\n  var keys = Object.keys(mainFields);\n  var vals = Object.values(mainFields);\n\n  var xml = '';\n  xml = xml + '<?xml version=\"1.0\"?>';\n  xml = xml + '<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\">';\n  xml = xml + '<Worksheet ss:Name=\"Fields\"><Table>';\n  xml = xml + '<Row>';\n  for (var n = 0; n < keys.length; n++) {\n    xml = xml + '<Cell><Data ss:Type=\"String\">' + keys[n] + '</Data></Cell>';\n  }\n  xml = xml + '</Row>';\n  xml = xml + '<Row>';\n  for (var p = 0; p < vals.length; p++) {\n    xml = xml + '<Cell><Data ss:Type=\"String\">' + vals[p] + '</Data></Cell>';\n  }\n  xml = xml + '</Row>';\n  xml = xml + '</Table></Worksheet>';\n\n  if (detailHeaders.length > 0) {\n    xml = xml + '<Worksheet ss:Name=\"Product Details Table\"><Table>';\n    xml = xml + '<Row>';\n    for (var q = 0; q < detailHeaders.length; q++) {\n      xml = xml + '<Cell><Data ss:Type=\"String\">' + detailHeaders[q] + '</Data></Cell>';\n    }\n    xml = xml + '</Row>';\n    for (var r = 0; r < detailRows.length; r++) {\n      xml = xml + '<Row>';\n      for (var s = 0; s < detailRows[r].length; s++) {\n        xml = xml + '<Cell><Data ss:Type=\"String\">' + detailRows[r][s] + '</Data></Cell>';\n      }\n      xml = xml + '</Row>';\n    }\n    xml = xml + '</Table></Worksheet>';\n  }\n\n  xml = xml + '</Workbook>';\n\n  return [{\n    binary: {\n      data: {\n        data: Buffer.from(xml, 'utf-8').toString('base64'),\n        mimeType: 'application/vnd.ms-excel',\n        fileName: 'data.xls'\n      }\n    }\n  }];"
      },
      "typeVersion": 2
    },
    {
      "id": "d2224313-0c64-4b3e-9d0d-327e184eff88",
      "name": "Upload the extracted result document",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        2592,
        288
      ],
      "parameters": {
        "name": "=n8n_laiye_adp_{{ $('Gmail Trigger').item.json.from.value[0].name }}_{{ $('Filter documents').item.json.attachment_name }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.id }}"
        },
        "inputDataFieldName": "=data"
      },
      "typeVersion": 3
    },
    {
      "id": "3b44154e-b2cb-45c7-8b89-84584a0681e8",
      "name": "Create a pending folder",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1952,
        640
      ],
      "parameters": {
        "name": "Untreated document",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "list",
          "value": "root",
          "cachedResultName": "/ (Root folder)"
        },
        "resource": "folder"
      },
      "typeVersion": 3
    },
    {
      "id": "34d8cd58-5538-40cd-8525-b4f67029b927",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        208,
        320
      ],
      "parameters": {
        "width": 528,
        "height": 560,
        "content": "## How it works\nThis workflow automates the full invoice processing pipeline: it monitors Gmail for invoice emails, extracts key data from multiple file formats, and automatically organizes files into Google Drive. It replaces manual invoice sorting, checking, and data entry.\n\n## Setup steps\n\n**Step 1:** \nConnect your Gmail account. The workflow automatically processes emails that match these rules:\n- Filename includes: invoice, receipt, expenses, or fee\n- File size < 50MB\n- Supported formats: jpeg, jpg, png, bmp, tiff, pdf, doc, docx, xls, xlsx\n- Default schedule: once per day (adjustable in the Gmail Trigger)\n\n\n**Step 2:**\nConnect your Google Drive.\n- Valid invoices are extracted and saved as Excel.\n- Unqualified files are stored in a folder named \u201cUntreated document\u201d for later manual review."
      },
      "typeVersion": 1
    },
    {
      "id": "a050a268-fc08-4eea-b6cf-4a1178e539ac",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        816,
        656
      ],
      "parameters": {
        "color": 7,
        "width": 272,
        "height": 112,
        "content": "**Step 1: Connect your Gmail account.**\n\nYou can set the execution frequency here. The default is once per day."
      },
      "typeVersion": 1
    },
    {
      "id": "1e948b3e-5089-4b3a-b9b1-0f39f19a3c3c",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        240
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 224,
        "content": "**File Filter Tips:**\n\n1. You can edit``` {{ $json.attachment_name }}``` to use your own filter keywords.\n2. We recommend not modifying file size and format settings to ensure smooth execution with the Laiye ADP nodes."
      },
      "typeVersion": 1
    },
    {
      "id": "be830e84-3cf6-4480-bbf5-ade1c44f69ea",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1840,
        256
      ],
      "parameters": {
        "color": 7,
        "width": 976,
        "height": 560,
        "content": ""
      },
      "typeVersion": 1
    },
    {
      "id": "08414006-ae87-4ff1-bc81-6c004d8edc8b",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1840,
        848
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 80,
        "content": "**Step 2: Connect your Google Drive.**\n\nEnsure all files are safely stored in Google Drive."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "18039fcc-f568-4c58-bbfb-ed5187be221b",
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Upload Unprocessed file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail Trigger": {
      "main": [
        [
          {
            "node": "Extract attachment information",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter documents": {
      "main": [
        [
          {
            "node": "Base64 Encode Document",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create a pending folder",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Result Processor": {
      "main": [
        [
          {
            "node": "Upload the extracted result document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Base64 Encode Document": {
      "main": [
        [
          {
            "node": "Laiye ADP HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Laiye ADP HTTP Request": {
      "main": [
        [
          {
            "node": "Result Processor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create a pending folder": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Extract attachment information": {
      "main": [
        [
          {
            "node": "Filter documents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

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

About this workflow

Our invoice extraction workflow is completed end-to-end automatically: Gmail invoice email screening → extraction of key fields from multi-format invoices → automatic archiving of results to Google Drive, replacing the repetitive manual labor of finance staff in opening and…

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

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

This template is built to be customized for your specific needs. This template has the core logic and n8n node specific references sorted to work with dynamic file names throughout the workflow. Store

Gmail, Slack, Gmail Trigger +3
Email & Gmail

Save N8N Cloud Invoices Received In Gmail In Google Drive. Uses gmailTrigger, splitOut, noOp, stickyNote. Event-driven trigger; 13 nodes.

Gmail Trigger, HTTP Request, Google Drive
Email & Gmail

This template is for everyone that wants to download their n8n Cloud invoices automatically as a PDF instead of downloading them manually.

Gmail Trigger, HTTP Request, Google Drive
Email & Gmail

Email AI Auto-responder. Summerize and send email. Uses emailReadImap, emailSend, httpRequest, googleDrive. Event-driven trigger; 78 nodes.

Email Read Imap, Email Send, HTTP Request +2
Email & Gmail

Receive any business document via email. The attachment is automatically classified (Invoice, Contract, or Purchase Order) using easybits Extractor, then routed down the correct path where a second Ex

@Easybits/N8N Nodes Extractor, Gmail Trigger, Google Drive +2