This workflow corresponds to n8n.io template #14180 — we link there as the canonical source.
This workflow follows the Gmail Trigger → Google Sheets 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 →
{
"meta": {
"templateCredsSetupCompleted": false
},
"name": "Invoice Duplicate Checker v2",
"tags": [],
"nodes": [
{
"id": "4477bb85-84c0-41a0-a05c-6b3bac0698f3",
"name": "Check Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
-528,
112
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Master Finance File"
}
},
"typeVersion": 4,
"alwaysOutputData": true
},
{
"id": "51fa97a3-3b7f-42ea-9ead-4a5a9e239803",
"name": "Already Exists?",
"type": "n8n-nodes-base.if",
"position": [
-208,
112
],
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.duplicate }}",
"value2": "true"
}
]
}
},
"typeVersion": 1
},
{
"id": "7553896e-f939-4262-af1f-756b25c5be25",
"name": "Add to Master List",
"type": "n8n-nodes-base.googleSheets",
"position": [
64,
256
],
"parameters": {
"columns": {
"value": {
"Invoice Number": "={{ $('HTTP Request').first().json.data.invoice_number }}",
"Final Amount (EUR)": "={{ $('HTTP Request').first().json.data.total_amount }}"
},
"schema": [
{
"id": "Invoice Number",
"type": "string",
"display": true,
"required": false,
"displayName": "Invoice Number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Original Amount",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Original Amount",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Currency",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Currency",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Exchange Rate",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Exchange Rate",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Final Amount (EUR)",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Final Amount (EUR)",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Master Finance File"
}
},
"typeVersion": 4
},
{
"id": "2fd986c1-a8b6-4adc-aec6-7daf8685295c",
"name": "Gmail Trigger",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
-1328,
112
],
"parameters": {
"simple": false,
"filters": {
"labelIds": []
},
"options": {
"downloadAttachments": true
},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
}
},
"typeVersion": 1.3
},
{
"id": "e5527bfa-9a24-4208-b1e3-ca695830d977",
"name": "Extract from File",
"type": "n8n-nodes-base.extractFromFile",
"position": [
-1088,
112
],
"parameters": {
"options": {},
"operation": "binaryToPropery",
"binaryPropertyName": "=attachment_0"
},
"typeVersion": 1.1
},
{
"id": "61cb08f9-ad41-4d68-b46d-3e44218464a2",
"name": "Edit Fields",
"type": "n8n-nodes-base.set",
"position": [
-928,
112
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "61f1027c-dc46-49a0-8450-ed63cd67e8c9",
"name": "data",
"type": "string",
"value": "=data:application/pdf;base64,{{ $json.data }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "2b847aff-69f7-4240-b1b2-5af669594158",
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"position": [
-768,
112
],
"parameters": {
"url": "https://extractor.easybits.tech/api/pipelines/YOUR_PIPELINE_ID",
"method": "POST",
"options": {},
"jsonBody": "={\n \"files\": [\n \"{{ $json.data }}\"\n ]\n} ",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpBearerAuth"
},
"typeVersion": 4.3
},
{
"id": "f6d5d00e-1e16-4fe7-bfb5-a5313c1b9d86",
"name": "Code in JavaScript",
"type": "n8n-nodes-base.code",
"position": [
-368,
112
],
"parameters": {
"jsCode": "const invoiceNumber = $('HTTP Request').first().json.data.invoice_number;\nconst rows = $input.all();\n\n// Debug: log what we're working with\nconst allInvoiceNumbers = rows.map(row => row.json[\"Invoice Number\"]).filter(Boolean);\n\nconst match = rows.filter(row => {\n const cellValue = row.json[\"Invoice Number\"];\n if (!cellValue) return false;\n return String(cellValue).trim() === String(invoiceNumber).trim();\n});\n\nreturn [{\n json: {\n duplicate: match.length > 0 ? \"true\" : \"false\",\n invoice_number: invoiceNumber,\n found_in_sheet: allInvoiceNumbers,\n rows_checked: rows.length\n }\n}];"
},
"typeVersion": 2
},
{
"id": "1b20a65c-608c-4118-a914-5141edab83ac",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1392,
-48
],
"parameters": {
"color": 7,
"width": 224,
"height": 336,
"content": "## \ud83d\udce7 Email Intake\nPolls Gmail every minute for emails with the **invoice** label.\nDownloads attachments automatically."
},
"typeVersion": 1
},
{
"id": "bf9d9a2f-5a9c-404b-b926-2e508a183224",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1152,
-48
],
"parameters": {
"color": 7,
"width": 544,
"height": 336,
"content": "## \ud83d\udcc4 Invoice Extraction with easybits\nExtracts the PDF attachment, converts it to base64, and sends it to the **easybits' extractor API** to pull structured invoice data (invoice number, amount, etc.)."
},
"typeVersion": 1
},
{
"id": "22ca9538-3bad-4cda-a10a-ae8ec96f79d8",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-592,
-48
],
"parameters": {
"color": 7,
"width": 544,
"height": 336,
"content": "## \ud83d\udd0d Duplicate Check\nReads all rows from the **Master Finance File** in Google Sheets. A Code node compares the extracted invoice number against existing entries. Returns `duplicate: true/false`."
},
"typeVersion": 1
},
{
"id": "3d6bc7f4-f812-49a4-a23e-0a63f8b39e29",
"name": "Slack: Alert Finance",
"type": "n8n-nodes-base.slack",
"position": [
64,
-64
],
"parameters": {
"text": "=\ud83d\udea8 *Duplicate Invoice Detected* Invoice number {{ $('HTTP Request').first().json.data.invoice_number }} was already submitted. Please review before processing.",
"user": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": ""
},
"select": "user",
"otherOptions": {}
},
"typeVersion": 2
},
{
"id": "e4f0a16b-8041-4202-989b-63a647a64728",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-32,
-208
],
"parameters": {
"color": 7,
"width": 304,
"height": 304,
"content": "## \ud83d\udea8 Duplicate Found\nIf the invoice **already exists** in the sheet, a Slack DM is sent to the user with the duplicate invoice number for manual review."
},
"typeVersion": 1
},
{
"id": "1b22bf73-e06f-40af-95d8-09564226374b",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-32,
112
],
"parameters": {
"color": 7,
"width": 304,
"height": 304,
"content": "## \u2705 New Invoice\nIf the invoice is **not** a duplicate, it gets appended to the Master Finance File in Google Sheets with the invoice number and total amount."
},
"typeVersion": 1
},
{
"id": "4d296449-cb5c-466e-b618-a39fe24ee6f0",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2192,
-304
],
"parameters": {
"width": 784,
"height": 832,
"content": "# \ud83d\udd0d Invoice Duplicate Checker\n\n## How It Works\nThis workflow automatically detects duplicate invoices from Gmail. Incoming PDF attachments are scanned by the easybits data extraction solution, then checked against the Master Finance File in Google Sheets. Duplicates trigger a Slack alert \u2013 new invoices get added to the sheet.\n\n**Flow overview:**\n1. Gmail picks up new emails labeled as invoices (polls every minute)\n2. The PDF attachment is extracted and converted to base64\n3. easybits Extractor reads the document and returns structured data\n4. The invoice number is compared against all existing entries in Google Sheets\n5. If duplicate \u2192 Slack DM alert to user\n6. If new \u2192 Invoice is appended to the Master Finance File\n\n---\n\n## Setup Guide\n\n**1. easybits Extractor** \u2013 Create a pipeline at extractor.easybits.tech with fields: `invoice_number` (String) and `total_amount` (Number). Copy your Pipeline ID and API Key.\n\n**2. HTTP Request node** \u2013 Replace the Pipeline ID in the URL. Add a Bearer Auth credential with your API Key.\n\n**3. Gmail Trigger** \u2013 Connect via OAuth2. Create an \"invoice\" label in Gmail and set it as the filter. Enable Download Attachments.\n\n**4. Google Sheets** \u2013 Connect both sheet nodes via OAuth2. Select your spreadsheet. Required columns: **Invoice Number** and **Final Amount (EUR)**.\n\n**5. Slack** \u2013 Create a Slack App at api.slack.com/apps. Add scopes: `chat:write`, `chat:write.public`, `channels:read`, `groups:read`, `users:read`, `users.profile:read`. Install via Settings \u2192 Install App. Add the Bot Token as a Slack API credential in n8n.\n\n**6. Activate** \u2013 Toggle the workflow on, label an invoice email, and test. Send the same invoice twice to verify the duplicate alert."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"availableInMCP": false,
"executionOrder": "v1"
},
"connections": {
"Edit Fields": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Check Google Sheets",
"type": "main",
"index": 0
}
]
]
},
"Gmail Trigger": {
"main": [
[
{
"node": "Extract from File",
"type": "main",
"index": 0
}
]
]
},
"Already Exists?": {
"main": [
[
{
"node": "Slack: Alert Finance",
"type": "main",
"index": 0
}
],
[
{
"node": "Add to Master List",
"type": "main",
"index": 0
}
]
]
},
"Extract from File": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Already Exists?",
"type": "main",
"index": 0
}
]
]
},
"Check Google Sheets": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automatically detects duplicate invoices from Gmail. Incoming PDF attachments are scanned by the easybits AI Extractor, then checked against the Master Finance File in Google Sheets. Duplicates trigger a Slack alert – new invoices get added to the sheet.
Source: https://n8n.io/workflows/14180/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
📘 Description
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
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
Automatically convert Gmail emails and Slack messages into Zendesk support tickets with intelligent priority detection, comprehensive Google Sheets tracking, and real-time team notifications. Streamli
This n8n workflow is designed for IT security professionals, email administrators, and organizations that want to automatically scan URLs received in emails for potential security threats. It provides