8
n8n 한국어amn8n.com

제출용 인보이스 처리 시스템

고급

이것은Invoice Processing, AI Summarization분야의자동화 워크플로우로, 24개의 노드를 포함합니다.주로 If, Code, Xero, Gmail, Slack 등의 노드를 사용하며. Gmail, OCR.space, Slack 및 Xero를 통한 인보이스 처리 자동화

사전 요구사항
  • Google 계정 및 Gmail API 인증 정보
  • Slack Bot Token 또는 Webhook URL
  • 대상 API의 인증 정보가 필요할 수 있음
  • Google Sheets API 인증 정보
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
  "id": "QHTTTSIjepK3ipPO",
  "meta": {
    "instanceId": "a9990cea8ba499cd2caa870c8ec5e719a88b31e6ed62907b6084053e5dc1857a",
    "templateCredsSetupCompleted": true
  },
  "name": "Invoice Processing System for submission",
  "tags": [
    {
      "id": "CMJL1dF3mMlRu0Jb",
      "name": "Invoice Processing",
      "createdAt": "2025-10-11T20:13:04.997Z",
      "updatedAt": "2025-10-11T20:13:04.997Z"
    }
  ],
  "nodes": [
    {
      "id": "189e9199-b377-4acc-a0e7-d5769e69a762",
      "name": "Gmail 트리거",
      "type": "n8n-nodes-base.gmailTrigger",
      "position": [
        -96,
        16
      ],
      "parameters": {
        "simple": false,
        "filters": {
          "q": "has:attachment filename:pdf"
        },
        "options": {
          "downloadAttachments": true
        },
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        }
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "d2kfFLUV2qlW2o43",
          "name": "Gmail account"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "7c5278ea-c32f-4f55-9ba4-4448c06c21f0",
      "name": "JavaScript 코드",
      "type": "n8n-nodes-base.code",
      "position": [
        80,
        16
      ],
      "parameters": {
        "jsCode": "// Get email data\nconst email = $input.item.json;\nconst binary = $input.item.binary;\n\n// Check if we have binary attachments\nif (!binary || Object.keys(binary).length === 0) {\n  return { \n    json: { \n      error: 'No invoice attachment found',\n      emailId: email.id,\n      emailSubject: email.subject\n    } \n  };\n}\n\n// Find first PDF or image attachment\nlet invoiceFile = null;\nlet invoiceKey = null;\n\nfor (const key in binary) {\n  const file = binary[key];\n  if (file.mimeType?.includes('pdf') || \n      file.mimeType?.includes('image') ||\n      file.mimeType?.includes('png') ||\n      file.mimeType?.includes('jpg')) {\n    invoiceFile = file;\n    invoiceKey = key;\n    break;\n  }\n}\n\nif (!invoiceFile) {\n  return { \n    json: { \n      error: 'No PDF or image attachment found',\n      emailId: email.id,\n      emailSubject: email.subject\n    } \n  };\n}\n\n// The binary data in n8n is already base64 encoded\n// Just use it directly without re-encoding\nlet base64Data = invoiceFile.data;\n\n// If it's a Buffer, convert to base64 string\nif (Buffer.isBuffer(base64Data)) {\n  base64Data = base64Data.toString('base64');\n}\n\n// Build the data URL for OCR.space\nconst dataUrl = `data:${invoiceFile.mimeType};base64,${base64Data}`;\n\nreturn {\n  json: {\n    emailId: email.id,\n    emailSubject: email.subject,\n    emailFrom: email.from,\n    emailDate: email.date,\n    attachmentData: base64Data,\n    attachmentName: invoiceFile.fileName,\n    mimeType: invoiceFile.mimeType,\n    dataUrl: dataUrl\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "4ddb1479-1b7f-4f8d-ad41-1a92654c98c8",
      "name": "시트에 행 추가 또는 업데이트",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1296,
        -48
      ],
      "parameters": {
        "columns": {
          "value": {
            "Amount": "={{ $json.amount }}",
            "Status": "Pending",
            "Vendor": "={{ $json.vendor }}",
            "Currency": "={{ $json.currency }}",
            "Due Date": "={{ $json.dueDate }}",
            "Email ID": "={{ $node[\"Code in JavaScript\"].json.emailId }}",
            "Timestamp": "={{ $now.format('yyyy-MM-dd HH:mm:ss') }}",
            "Description": "={{ $json.description }}",
            "Invoice Date": "={{ $json.invoiceDate }}",
            "Invoice Number": "={{ $json.invoiceNumber }}"
          },
          "schema": [
            {
              "id": "Invoice Number",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Invoice Number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Vendor",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Vendor",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Amount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Amount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Currency",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Currency",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Invoice Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Invoice Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Due Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Due Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Processed By",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Processed By",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Invoice Number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1TWE856dG0g0bSRr3M4SKPcpnJ32veWlq3m_DWJoSaTQ/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "=\"YOUR_SPREADSHEET_ID\""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "hVo2bSobLUbf5b3a",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "abea5730-c60f-47f4-83b3-50cb42c6c9e0",
      "name": "Slack 승인 요청",
      "type": "n8n-nodes-base.slack",
      "position": [
        1904,
        -48
      ],
      "webhookId": "4f4fa1f8-f072-4ceb-b802-68904622847d",
      "parameters": {
        "select": "channel",
        "message": "=🧾 *New Invoice Requires Approval*\n\n*Vendor:* {{ $json.vendor }}\n*Amount:* ${{ $json.amount }} {{ $json.currency }}\n*Invoice #:* {{ $json.invoiceNumber }}\n*Due Date:* {{ $json.dueDate }}\n*Description:* {{ $json.description }}",
        "options": {},
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09LDKZJGBB",
          "cachedResultName": "invoice-approvals"
        },
        "operation": "sendAndWait",
        "authentication": "oAuth2",
        "approvalOptions": {
          "values": {
            "approvalType": "double"
          }
        }
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "plTTzKNjrWG2CYjh",
          "name": "Slack account"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "955f6439-8d79-4f9f-b35c-7bf3e39f6aad",
      "name": "Slack 거부 메시지",
      "type": "n8n-nodes-base.slack",
      "position": [
        1296,
        144
      ],
      "webhookId": "e4b6a100-ab6c-4908-8909-a37c5c210aad",
      "parameters": {
        "text": "=Channel: #invoice-approvals\n\nText:\n❌ *Invoice Auto-Rejected*\n\nAn invoice was rejected by AI analysis:\n\n- From: {{ $node[\"Code in JavaScript\"].json.emailFrom }}\n- Subject: {{ $node[\"Code in JavaScript\"].json.emailSubject }}\n- Reason: Did not pass qualification checks\n- Confidence: {{ $json.confidence }}%\n- Red Flags: {{ $json.redFlags.join(', ') }}\n\nThe email has been labeled \"Rejected\" in Gmail.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09LDKZJGBB",
          "cachedResultName": "invoice-approvals"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "plTTzKNjrWG2CYjh",
          "name": "Slack account"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "424382f8-ff99-457e-9714-569c83e29157",
      "name": "자격 확인",
      "type": "n8n-nodes-base.if",
      "position": [
        1040,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1237bc0c-6f1a-4a3b-98ff-45369c8ebc06",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.qualified }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "be9d4c45-2bb3-4ca4-944e-65045fa75a60",
      "name": "인보이스 데이터 파싱",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        0
      ],
      "parameters": {
        "jsCode": "// Get OCR result\nconst ocrResult = $json;\n\n// Extract the text\nlet extractedText = '';\n\nif (ocrResult.ParsedResults && ocrResult.ParsedResults[0]) {\n  extractedText = ocrResult.ParsedResults[0].ParsedText;\n} else {\n  return {\n    json: {\n      error: 'OCR failed to extract text',\n      qualified: false,\n      redFlags: ['Could not read invoice text']\n    }\n  };\n}\n\n// Helper function to parse dates correctly without timezone issues\nfunction parseDate(dateString) {\n  // Try to parse dates like \"October 1, 2025\"\n  const date = new Date(dateString + ' 12:00:00 UTC'); // Add noon UTC to avoid timezone shifts\n  \n  if (!isNaN(date)) {\n    const year = date.getUTCFullYear();\n    const month = String(date.getUTCMonth() + 1).padStart(2, '0');\n    const day = String(date.getUTCDate()).padStart(2, '0');\n    return `${year}-${month}-${day}`;\n  }\n  \n  return '';\n}\n\n// Parse invoice fields using regex\nconst invoiceData = {\n  qualified: true,\n  vendor: '',\n  invoiceNumber: '',\n  amount: 0,\n  currency: 'USD',\n  invoiceDate: '',\n  dueDate: '',\n  description: '',\n  confidence: 100,\n  redFlags: []\n};\n\n// Extract Vendor (line after \"From:\")\nconst vendorMatch = extractedText.match(/From:\\s*([^\\n]+)/i);\nif (vendorMatch) {\n  invoiceData.vendor = vendorMatch[1].trim();\n}\n\n// Extract Invoice Number\nconst invoiceNumMatch = extractedText.match(/Invoice Number:\\s*([^\\n]+)/i);\nif (invoiceNumMatch) {\n  invoiceData.invoiceNumber = invoiceNumMatch[1].trim();\n}\n\n// Extract Amount (from \"Total:\" line) - CRITICAL!\nconst totalMatch = extractedText.match(/Total:\\s*\\$\\s*([0-9,]+\\.?[0-9]*)/i);\nif (totalMatch) {\n  invoiceData.amount = parseFloat(totalMatch[1].replace(/,/g, ''));\n}\n\n// If Total not found, try Amount line\nif (invoiceData.amount === 0) {\n  const amountMatch = extractedText.match(/Amount:\\s*\\$\\s*([0-9,]+\\.?[0-9]*)/i);\n  if (amountMatch) {\n    invoiceData.amount = parseFloat(amountMatch[1].replace(/,/g, ''));\n  }\n}\n\n// Extract Currency\nconst currencyMatch = extractedText.match(/(USD|EUR|GBP|CAD)/i);\nif (currencyMatch) {\n  invoiceData.currency = currencyMatch[1].toUpperCase();\n}\n\n// Extract Invoice Date - FIXED!\nconst dateMatch = extractedText.match(/Date:\\s*([^\\n]+)/i);\nif (dateMatch) {\n  const dateStr = dateMatch[1].trim();\n  invoiceData.invoiceDate = parseDate(dateStr);\n}\n\n// Extract Due Date - FIXED!\nconst dueMatch = extractedText.match(/Due Date:\\s*([^\\n]+)/i);\nif (dueMatch) {\n  const dateStr = dueMatch[1].trim();\n  invoiceData.dueDate = parseDate(dateStr);\n}\n\n// Extract Description\nconst descMatch = extractedText.match(/Description:\\s*([^\\n]+)/i);\nif (descMatch) {\n  invoiceData.description = descMatch[1].trim();\n}\n\n// Validation checks\nif (!invoiceData.vendor) {\n  invoiceData.qualified = false;\n  invoiceData.redFlags.push('Missing vendor name');\n}\n\nif (!invoiceData.invoiceNumber) {\n  invoiceData.qualified = false;\n  invoiceData.redFlags.push('Missing invoice number');\n}\n\nif (invoiceData.amount === 0) {\n  invoiceData.qualified = false;\n  invoiceData.redFlags.push('Missing or invalid amount');\n}\n\nif (invoiceData.amount > 10000) {\n  invoiceData.qualified = false;\n  invoiceData.redFlags.push('Amount exceeds $10,000');\n}\n\nreturn {\n  json: invoiceData\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "36afc95e-af94-4c98-a458-744e9bfd1139",
      "name": "Slack용 형식 지정",
      "type": "n8n-nodes-base.code",
      "position": [
        1504,
        -48
      ],
      "parameters": {
        "jsCode": "const sheetData = $node[\"Append or update row in sheet\"].json;\n\nreturn {\n  json: {\n    vendor: sheetData.Vendor,\n    amount: sheetData.Amount,\n    currency: sheetData.Currency,\n    invoiceNumber: sheetData[\"Invoice Number\"],\n    invoiceDate: sheetData[\"Invoice Date\"],  // ← ADD THIS LINE!\n    dueDate: sheetData[\"Due Date\"],\n    description: sheetData.Description\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "b524a093-e827-4049-b0c9-809dc839465e",
      "name": "거부 메시지",
      "type": "n8n-nodes-base.slack",
      "position": [
        2896,
        16
      ],
      "webhookId": "019dcb52-375d-4fff-9e0d-d4d5b4ac0282",
      "parameters": {
        "text": "❌ Invoice rejected",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09LDKZJGBB",
          "cachedResultName": "invoice-approvals"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "plTTzKNjrWG2CYjh",
          "name": "Slack account"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "afcf2f08-41ec-4ce5-bb32-98733317379f",
      "name": "성공 메시지",
      "type": "n8n-nodes-base.slack",
      "position": [
        3248,
        -176
      ],
      "webhookId": "ae7fc4b9-99a3-41ad-ba4e-4e4a15a52303",
      "parameters": {
        "text": "✅ Invoice posted to Xero",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09LDKZJGBB",
          "cachedResultName": "invoice-approvals"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "plTTzKNjrWG2CYjh",
          "name": "Slack account"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "2a70f5c0-db5f-44eb-8eb8-24c978892049",
      "name": "거부 상태 업데이트",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2704,
        16
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "Rejected",
            "Invoice Number": "={{ $('Clean Invoice Payload').item.json.invoiceNumber }}"
          },
          "schema": [
            {
              "id": "Invoice Number",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Invoice Number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Vendor",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Vendor",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Amount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Amount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Currency",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Currency",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Invoice Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Invoice Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Due Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Due Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Processed By",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Processed By",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Invoice Number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1TWE856dG0g0bSRr3M4SKPcpnJ32veWlq3m_DWJoSaTQ/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "=\"YOUR_SPREADSHEET_ID\""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "hVo2bSobLUbf5b3a",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "f775d6e5-a97b-4aea-be73-f6a3030cbfc3",
      "name": "승인 상태 업데이트",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        3024,
        -176
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "Approved",
            "Invoice Number": "={{ $('Clean Invoice Payload').item.json.invoiceNumber }}"
          },
          "schema": [
            {
              "id": "Invoice Number",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Invoice Number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Vendor",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Vendor",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Amount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Amount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Currency",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Currency",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Invoice Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Invoice Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Due Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Due Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Email ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Email ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Processed By",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Processed By",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Invoice Number"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "=Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "=\"YOUR_SPREADSHEET_ID\""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "hVo2bSobLUbf5b3a",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "b062f234-61a1-4913-8135-be9d9be322a8",
      "name": "처리됨 레이블 추가",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2832,
        -176
      ],
      "webhookId": "a5cbed4c-0c43-4bf0-8114-e8fbbf987e9e",
      "parameters": {
        "labelIds": [
          "Label_3710233975993850496"
        ],
        "messageId": "={{$node[\"Code in JavaScript\"].json.emailId}}",
        "operation": "addLabels"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "d2kfFLUV2qlW2o43",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "854afcd4-6559-4753-b75d-2a65766c492d",
      "name": "거부됨 레이블 추가",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2432,
        16
      ],
      "webhookId": "2e5f020d-ed42-438c-8744-4fdd602145c7",
      "parameters": {
        "labelIds": [
          "Label_8899302689673155790"
        ],
        "messageId": "={{$node[\"Code in JavaScript\"].json.emailId}}",
        "operation": "addLabels"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "d2kfFLUV2qlW2o43",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "025efb16-32dd-4a7e-8349-baef18b32040",
      "name": "여러 연락처 가져오기",
      "type": "n8n-nodes-base.xero",
      "position": [
        2416,
        -176
      ],
      "parameters": {
        "limit": 1,
        "options": {},
        "resource": "contact",
        "operation": "getAll",
        "organizationId": "=9a46b4a9-feac-446e-a04d-4e7421565898"
      },
      "credentials": {
        "xeroOAuth2Api": {
          "id": "XfSNoqDEDyivPRYX",
          "name": "Xero account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b43a1017-8ecb-4c39-b92f-8865349ccec2",
      "name": "인보이스 페이로드 정리",
      "type": "n8n-nodes-base.code",
      "position": [
        1696,
        -48
      ],
      "parameters": {
        "jsCode": "const invoiceData = { ...$node[\"Format for Slack\"].json };\n\n// Strip off any vendor or itemCode field that might be passed\ndelete invoiceData.vendor;\ndelete invoiceData.itemCode;\ndelete invoiceData[\"Item Code Name or ID\"]; // Just in case\n\nreturn [{ json: invoiceData }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a77a3860-e69a-428f-b74e-d926859c83e3",
      "name": "인보이스 생성",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2624,
        -176
      ],
      "parameters": {
        "url": "https://api.xero.com/api.xro/2.0/Invoices",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"Type\": \"ACCPAY\",\n  \"Contact\": {\n    \"ContactID\": \"{{$node[\"Get many contacts\"].json.ContactID}}\"\n  },\n  \"Date\": \"{{$node[\"Format for Slack\"].json.invoiceDate}}\",\n  \"DueDate\": \"{{$node[\"Format for Slack\"].json.dueDate}}\",\n  \"InvoiceNumber\": \"{{$node[\"Format for Slack\"].json.invoiceNumber}}\",\n  \"Reference\": \"{{$node[\"Format for Slack\"].json.invoiceNumber}}\",\n  \"Status\": \"DRAFT\",\n  \"LineAmountTypes\": \"Exclusive\",\n  \"LineItems\": [\n    {\n      \"Description\": \"{{$node[\"Format for Slack\"].json.description}}\",\n      \"Quantity\": 1,\n      \"UnitAmount\": {{$node[\"Format for Slack\"].json.amount}},\n      \"AccountCode\": \"463\",\n      \"TaxType\": \"INPUT2\"\n    }\n  ]\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Accept",
              "value": "application/json"
            },
            {
              "name": "xero-tenant-id",
              "value": "=\"{{$credentials.xero.apiKey}}\""
            }
          ]
        },
        "nodeCredentialType": "xeroOAuth2Api"
      },
      "credentials": {
        "xeroOAuth2Api": {
          "id": "XfSNoqDEDyivPRYX",
          "name": "Xero account"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "270ea877-b081-4704-8137-3cbcf62606fb",
      "name": "유효한 첨부파일이 있나요?",
      "type": "n8n-nodes-base.if",
      "position": [
        288,
        16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "5b78e677-d068-411a-8c43-e41a9725f370",
              "operator": {
                "type": "number",
                "operation": "notExists",
                "singleValue": true
              },
              "leftValue": "={{$json.error}}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "4128986d-09d7-44b5-a9f5-5f8a532c1712",
      "name": "승인되었나요?",
      "type": "n8n-nodes-base.if",
      "position": [
        2128,
        -48
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "b58f3729-809c-4b21-9210-6c581dbeed50",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.data.approved }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8400ed04-51ba-4cc6-92cc-abb03a09e258",
      "name": "텍스트 추출",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        544,
        0
      ],
      "parameters": {
        "url": "https://api.ocr.space/parse/image",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"name\": \"apikey\",\n  \"value\": \"{{$credentials.ocrspaceApi.apiKey}}\"\n}",
        "sendBody": true,
        "specifyBody": "json"
      },
      "typeVersion": 4.2
    },
    {
      "id": "d132fdec-f017-49f6-a4bc-8a938df37d98",
      "name": "스티커 메모",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -912,
        -464
      ],
      "parameters": {
        "width": 608,
        "height": 1696,
        "content": "### Automate Invoice Processing with Gmail, OCR.space, Slack & Xero\n\nThis n8n template demonstrates how to automatically extract, validate, approve, and sync invoices received via email using AI and automation — reducing manual processing time and minimizing errors.\n\n### Use Cases\n\n- Finance teams looking to eliminate invoice approval bottlenecks  \n- Businesses that receive PDF/image invoices via email  \n- Companies currently logging invoices manually into spreadsheets or accounting tools  \n\n### How It Works\n\n1. **Trigger**: Watches for new emails in Gmail with PDF/image attachments.  \n2. **OCR**: Sends the attachment to OCR.space API (https://ocr.space/OCRAPI) to extract invoice text.  \n3. **Parsing**: Extracts key fields:  \n   - Vendor  \n   - Invoice number  \n   - Amount  \n   - Currency  \n   - Invoice date  \n   - Due date  \n   - Description  \n4. **Validation Logic**:  \n   - Checks if amount is valid  \n   - Ensures vendor and invoice number are present  \n   - Flags high-value invoices (e.g., over $10,000)  \n5. **Routing**:  \n   - If invalid: \n   - Sends a Slack message highlighting issues\n   - Labels email as **Rejected**  \n   - If valid:  \n     - Logs the invoice into Google Sheets  \n     - Sends a Slack message to the finance team for approval  \n     - After approval, creates a draft invoice in Xero  \n     - Labels the email as **Processed** in Gmail  \n\n### Customisation Options\n\n- Swap Gmail trigger with a file uploader or webhook  \n- Adjust the Slack notification channels or approval thresholds  \n- Replace Xero with QuickBooks, Zoho, or another tool  \n- Adapt log storage from Google Sheets to Airtable or Notion  \n- Add a reminder flow if no approval is received within 24h  \n\n### Prerequisites/Credential Setup\n\nTo use this workflow securely, you'll need the following credentials set up in n8n:\n\n- **Gmail OAuth2** – to watch for incoming invoice emails and apply labels\n- **OCR.space API** – create an HTTP Credential in n8n named `ocrspaceApi`, with your OCR.space key\n- **Slack OAuth2 or Webhook (with connected bot)** – to send approval/rejection messages to your team\n- **Google Sheets OAuth2 credentials** – to log processed invoices for audit and tracking\n- **Xero OAuth2 credentials (via n8n’s native integration)** – connect your Xero account to push approved invoices in Draft status\n \n### Secure Configuration\n- All credential fields use **n8n Credential Types**  \n- No sensitive data (API keys, spreadsheet IDs) is hardcoded  \n\n### Why This Helps\n\n- Reduces time spent manually reviewing and copying invoices\n- Maintains structured logs with clean data\n- Automates Slack approvals and reduces email back-and-forth\n- Improves audit trails and AP visibility across tools\n\n---\nWith this template, finance teams can streamline invoice approvals, reduce processing time, and maintain a clean audit trail, all fully automated."
      },
      "typeVersion": 1
    },
    {
      "id": "b16d6e58-0125-4735-b939-040945fcc89b",
      "name": "스티커 메모1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1008,
        -336
      ],
      "parameters": {
        "width": 1248,
        "height": 640,
        "content": "## 2. Approve or Reject Invoice in Slack\n[Read more about Slack Approval workflows in n8n](https://docs.n8n.io/integrations/slack/)\n\nAfter parsing, the workflow evaluates whether the invoice meets preset criteria (e.g., approved vendors or amount limits). It logs the result in a Google Sheet and sends a Slack message for manual approval. Based on the response, it either proceeds to Xero or marks it as rejected.\n\nSetup Tip: Replace YOUR_SLACK_CLIENT_ID with the Client ID from your Slack apps page - https://api.slack.com/apps.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "af22b982-18c5-4adc-9e88-d7ecf53f357c",
      "name": "스티커 메모2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -144,
        -336
      ],
      "parameters": {
        "width": 1120,
        "height": 640,
        "content": "## 1. Extract & Parse Invoice from Gmail\n[Read more about the Gmail and HTTP Request nodes](https://docs.n8n.io/integrations/builtin/email-read-imap/)\n  \nThis section listens for new emails via Gmail, checks if an attachment exists and is valid, then extracts and parses invoice data using OCR (via OCR.Space API). The data is cleaned and prepared for downstream steps.\n\nSetup Tip: Replace YOUR_GOOGLE_CLIENT_ID with the Client ID from your Google Console - https://console.cloud.google.com/ for your Google Sheets, Google Docs, Gmail."
      },
      "typeVersion": 1
    },
    {
      "id": "422c5b66-d77a-4224-b689-e4b57152373e",
      "name": "스티커 메모3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2288,
        -336
      ],
      "parameters": {
        "width": 1168,
        "height": 640,
        "content": "## 3. Sync Outcome to Xero & Notify\n[Read more about Xero and Gmail automation](https://docs.n8n.io/integrations/xero/)\n\nIf the invoice is approved, the workflow fetches the contact from Xero, creates the invoice, updates Gmail labels, logs final status in Google Sheets, and sends a success message in Slack. If rejected, it applies a different Gmail label, updates status, and notifies the team.\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "a1d9da45-06e7-460c-9c56-bc430ee568b6",
  "connections": {
    "8400ed04-51ba-4cc6-92cc-abb03a09e258": {
      "main": [
        [
          {
            "node": "be9d4c45-2bb3-4ca4-944e-65045fa75a60",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "189e9199-b377-4acc-a0e7-d5769e69a762": {
      "main": [
        [
          {
            "node": "7c5278ea-c32f-4f55-9ba4-4448c06c21f0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4128986d-09d7-44b5-a9f5-5f8a532c1712": {
      "main": [
        [
          {
            "node": "025efb16-32dd-4a7e-8349-baef18b32040",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "854afcd4-6559-4753-b75d-2a65766c492d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "a77a3860-e69a-428f-b74e-d926859c83e3": {
      "main": [
        [
          {
            "node": "b062f234-61a1-4913-8135-be9d9be322a8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "36afc95e-af94-4c98-a458-744e9bfd1139": {
      "main": [
        [
          {
            "node": "b43a1017-8ecb-4c39-b92f-8865349ccec2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "025efb16-32dd-4a7e-8349-baef18b32040": {
      "main": [
        [
          {
            "node": "a77a3860-e69a-428f-b74e-d926859c83e3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "854afcd4-6559-4753-b75d-2a65766c492d": {
      "main": [
        [
          {
            "node": "2a70f5c0-db5f-44eb-8eb8-24c978892049",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7c5278ea-c32f-4f55-9ba4-4448c06c21f0": {
      "main": [
        [
          {
            "node": "270ea877-b081-4704-8137-3cbcf62606fb",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "be9d4c45-2bb3-4ca4-944e-65045fa75a60": {
      "main": [
        [
          {
            "node": "424382f8-ff99-457e-9714-569c83e29157",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b062f234-61a1-4913-8135-be9d9be322a8": {
      "main": [
        [
          {
            "node": "f775d6e5-a97b-4aea-be73-f6a3030cbfc3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "424382f8-ff99-457e-9714-569c83e29157": {
      "main": [
        [
          {
            "node": "4ddb1479-1b7f-4f8d-ad41-1a92654c98c8",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "955f6439-8d79-4f9f-b35c-7bf3e39f6aad",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b43a1017-8ecb-4c39-b92f-8865349ccec2": {
      "main": [
        [
          {
            "node": "abea5730-c60f-47f4-83b3-50cb42c6c9e0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "270ea877-b081-4704-8137-3cbcf62606fb": {
      "main": [
        [
          {
            "node": "8400ed04-51ba-4cc6-92cc-abb03a09e258",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "abea5730-c60f-47f4-83b3-50cb42c6c9e0": {
      "main": [
        [
          {
            "node": "4128986d-09d7-44b5-a9f5-5f8a532c1712",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "f775d6e5-a97b-4aea-be73-f6a3030cbfc3": {
      "main": [
        [
          {
            "node": "afcf2f08-41ec-4ce5-bb32-98733317379f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2a70f5c0-db5f-44eb-8eb8-24c978892049": {
      "main": [
        [
          {
            "node": "b524a093-e827-4049-b0c9-809dc839465e",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4ddb1479-1b7f-4f8d-ad41-1a92654c98c8": {
      "main": [
        [
          {
            "node": "36afc95e-af94-4c98-a458-744e9bfd1139",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
자주 묻는 질문

이 워크플로우를 어떻게 사용하나요?

위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.

이 워크플로우는 어떤 시나리오에 적합한가요?

고급 - 청구서 처리, AI 요약

유료인가요?

이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.

워크플로우 정보
난이도
고급
노드 수24
카테고리2
노드 유형9
난이도 설명

고급 사용자를 위한 16+개 노드의 복잡한 워크플로우

저자
Abi Odedeyi

Abi Odedeyi

@abiodedeyi

Automation Builder with extensive experience helping businesses streamline processes and generate revenue.

외부 링크
n8n.io에서 보기

이 워크플로우 공유

카테고리

카테고리: 34