This workflow follows the Gmail → Gmail 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 →
{
"name": "AI Expense Tracker",
"nodes": [
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"simple": false,
"filters": {
"labelIds": [
"Label_376844516545676179"
],
"readStatus": "unread"
},
"options": {
"downloadAttachments": true
}
},
"type": "n8n-nodes-base.gmailTrigger",
"typeVersion": 1.4,
"position": [
-32,
0
],
"id": "31279581-3df2-48c6-bb35-3e3044332eee",
"name": "Receive Expense Email"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "35f516d8-325f-4d89-8fc8-d28c47969734",
"name": "employee_email",
"value": "={{ $json.from?.value?.[0]?.address || $json.from || \"\" }}",
"type": "string"
},
{
"id": "34911229-6561-4a7f-83f1-c8d0393194a2",
"name": "email_subject",
"value": "={{ $json.subject || \"\" }}",
"type": "string"
},
{
"id": "82624752-9749-4dbd-9083-c268adfb6e05",
"name": "received_at",
"value": "={{ $now }}",
"type": "string"
},
{
"id": "62acbb47-74c4-45a3-bec0-039ea8203a5c",
"name": "file_name",
"value": "={{ $binary.attachment_0?.fileName || $binary.data?.fileName || \"\" }}",
"type": "string"
},
{
"id": "078bf747-f9c6-4761-912c-7a664a283278",
"name": "mime_type",
"value": "={{ $binary.attachment_0?.mimeType || $binary.data?.mimeType || \"\" }}",
"type": "string"
},
{
"id": "f41be5e5-a0f3-4bf6-bcae-9133e55b5c00",
"name": "binary_property",
"value": "={{ $binary.attachment_0 ? \"attachment_0\" : ($binary.data ? \"data\" : \"\") }}",
"type": "string"
}
]
},
"includeOtherFields": true,
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
144,
0
],
"id": "f3998946-21fa-4e84-bc66-8f125c842e89",
"name": "Normalize Email & Attachment"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "3e7b9e42-489e-45aa-a9bd-19c9fcf9811a",
"leftValue": "={{ $json.binary_property }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
320,
0
],
"id": "86012d11-ef54-4658-b216-f9c5d5b17542",
"name": "Has Attachment?"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "d3803e00-5e9b-421a-9363-dc7b675882ad",
"leftValue": "={{ [\"application/pdf\", \"image/jpeg\", \"image/png\"].includes($json.mime_type) }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.3,
"position": [
480,
-176
],
"id": "c43a41d3-ff57-4cd2-868d-0c2e67131296",
"name": "Validate File Type"
},
{
"parameters": {
"operation": "pdf",
"binaryPropertyName": "={{ $json.binary_property }}",
"options": {}
},
"type": "n8n-nodes-base.extractFromFile",
"typeVersion": 1.1,
"position": [
1024,
-240
],
"id": "e6378992-cf8b-4718-8501-fe5b8b3e0b57",
"name": "Extract Text from Receipt"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"leftValue": "={{ $json.mime_type }}",
"rightValue": "application/pdf",
"operator": {
"type": "string",
"operation": "equals"
},
"id": "637cc15c-6ad6-4edc-bf37-b92a31264ae4"
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "PDF"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "8ebcde8c-6bdc-41c6-9fb0-2bfca1cbbc9d",
"leftValue": "={{ $json.mime_type }}",
"rightValue": "image/jpeg",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "JPG"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "a87e5324-0e58-4a4e-b51f-61b1ca77de87",
"leftValue": "={{ $json.mime_type }}",
"rightValue": "image/png",
"operator": {
"type": "string",
"operation": "equals",
"name": "filter.operator.equals"
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "PNG"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
720,
-208
],
"id": "a785e4f5-bfd1-45b6-a4a7-4a6fdfb64017",
"name": "Route by File Type"
},
{
"parameters": {
"operation": "binaryToPropery",
"binaryPropertyName": "={{ $json.binary_property }}",
"options": {}
},
"type": "n8n-nodes-base.extractFromFile",
"typeVersion": 1.1,
"position": [
1008,
0
],
"id": "7755a169-9f45-4dca-9b87-cf85b5012423",
"name": "Extract from File"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "e154aee4-79d2-4bc2-b72a-deacf5bb06d3",
"name": "receipt_text",
"value": "={{ $json.text || $json.output?.[0]?.content?.[0]?.text || '' }}",
"type": "string"
},
{
"id": "7a3156ea-415c-4b6a-8cc2-99193831a047",
"name": "expense_id",
"value": "={{ \"EXP-\" + $now.toFormat(\"yyyyMMdd-HHmmss\") }}",
"type": "string"
},
{
"id": "63cd0dd9-e4c8-41b1-81cf-9cef2c9b944b",
"name": "employee_email",
"value": "={{ $('Normalize Email & Attachment').item.json.employee_email }}",
"type": "string"
},
{
"id": "fc1c227e-14bc-425f-bf00-6a7b4c25f755",
"name": "file_name",
"value": "={{ $('Normalize Email & Attachment').item.json.file_name }}",
"type": "string"
},
{
"id": "d76da55c-f1bd-4d82-a157-151d02e95022",
"name": "mime_type",
"value": "={{ $('Normalize Email & Attachment').item.json.mime_type }}",
"type": "string"
},
{
"id": "a802df7c-a86b-4948-86c6-1676c78f5f6d",
"name": "processed_at",
"value": "={{ $now }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1440,
0
],
"id": "3d3cc12a-8aad-4e55-9307-042757e4f922",
"name": "Prepare Expense Data"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "GPT-4.1-MINI"
},
"responses": {
"values": [
{
"content": "=Tu es un syst\u00e8me d\u2019extraction de notes de frais.\n\nAnalyse uniquement le texte fourni.\nN\u2019invente aucune information.\nLorsqu\u2019une valeur est absente ou incertaine, retourne null.\nRetourne uniquement un JSON valide, sans Markdown ni explication.\n\nCat\u00e9gories autoris\u00e9es :\nMEALS\nTRANSPORT\nHOTEL\nOFFICE_SUPPLIES\nSOFTWARE\nTELECOM\nFUEL\nOTHER\n\nMoyens de paiement autoris\u00e9s :\nCARD\nCASH\nBANK_TRANSFER\nMOBILE_PAYMENT\nOTHER\n\nStructure obligatoire :\n\n{\n \"merchant_name\": \"string|null\",\n \"expense_date\": \"YYYY-MM-DD|null\",\n \"total_amount\": \"number|null\",\n \"currency\": \"string|null\",\n \"tax_amount\": \"number|null\",\n \"category\": \"MEALS|TRANSPORT|HOTEL|OFFICE_SUPPLIES|SOFTWARE|TELECOM|FUEL|OTHER\",\n \"payment_method\": \"CARD|CASH|BANK_TRANSFER|MOBILE_PAYMENT|OTHER|null\",\n \"document_number\": \"string|null\",\n \"confidence\": 0.0,\n \"missing_fields\": [\"string\"]\n}\n\nR\u00e8gles :\n- confidence doit \u00eatre un nombre entre 0 et 1.\n- Utilise un code devise ISO comme MAD, EUR ou USD.\n- total_amount et tax_amount doivent \u00eatre des nombres, sans symbole mon\u00e9taire.\n- expense_date doit respecter YYYY-MM-DD.\n- missing_fields doit contenir les champs importants absents.\n- Ne d\u00e9duis la devise que lorsqu\u2019elle est clairement identifiable.\n\nTexte du document :\n\n{{ $json.receipt_text }}"
}
]
},
"builtInTools": {},
"options": {
"textFormat": {
"textOptions": {
"type": "json_object"
}
}
}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 2.3,
"position": [
1616,
0
],
"id": "255756e6-8f25-4cc9-a3ff-b1bb1b44fe51",
"name": "AI Expense Extractor"
},
{
"parameters": {
"jsCode": "const raw = $json.output?.[0]?.content?.[0]?.text ?? $json.text ?? $json;\n\nlet expense;\n\nif (typeof raw === 'string') {\n const cleaned = raw\n .replace(/^```json\\s*/i, '')\n .replace(/```$/i, '')\n .trim();\n\n try {\n expense = JSON.parse(cleaned);\n } catch (error) {\n throw new Error(`Invalid JSON returned by the AI: ${error.message}`);\n }\n} else {\n expense = raw;\n}\n\nreturn [{\n json: {\n ...expense,\n expense_id: $('Prepare Expense Data').first().json.expense_id,\n employee_email: $('Prepare Expense Data').first().json.employee_email,\n file_name: $('Prepare Expense Data').first().json.file_name,\n mime_type: $('Prepare Expense Data').first().json.mime_type,\n processed_at: $('Prepare Expense Data').first().json.processed_at,\n },\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1952,
0
],
"id": "df6b5d4f-dd23-4434-9365-de1d7984aa18",
"name": "Parse Expense JSON"
},
{
"parameters": {
"inputDataFieldName": "={{ $('Normalize Email & Attachment').first().json.binary_property }}",
"name": "={{ $json.file_name }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "YOUR_EXPENSES_FOLDER_ID",
"mode": "id",
"cachedResultName": "Select your expenses folder"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
2704,
-240
],
"id": "d9839444-6fba-4809-9814-16121eaba229",
"name": "Upload Receipt"
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst sourceItem = $('Normalize Email & Attachment').first();\n\nreturn items.map(item => ({\n json: item.json,\n binary: sourceItem.binary\n}));"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2480,
-240
],
"id": "48364a06-3a2b-49a1-8776-61df8b73224d",
"name": "Code in JavaScript"
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_EXPENSES_SPREADSHEET_ID",
"mode": "id",
"cachedResultName": "Select your expenses spreadsheet"
},
"sheetName": {
"__rl": true,
"value": "YOUR_EXPENSES_SHEET_ID",
"mode": "id",
"cachedResultName": "Select your expenses sheet"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"expense_id": "={{ $('Parse Expense JSON').first().json.expense_id }}",
"employee_email": "={{ $('Parse Expense JSON').first().json.employee_email }}",
"merchant_name": "={{ $('Parse Expense JSON').first().json.merchant_name }}",
"expense_date": "={{ $('Parse Expense JSON').first().json.expense_date }}",
"total_amount": "={{ $('Parse Expense JSON').first().json.total_amount }}",
"currency": "={{ $('Parse Expense JSON').first().json.currency }}",
"tax_amount": "={{ $('Parse Expense JSON').first().json.tax_amount }}",
"category": "={{ $('Parse Expense JSON').first().json.category }}",
"payment_method": "={{ $('Parse Expense JSON').first().json.payment_method }}",
"document_number": "={{ $('Parse Expense JSON').first().json.document_number }}",
"confidence": "={{ $('Parse Expense JSON').first().json.confidence }}",
"missing_fields": "={{ $('Parse Expense JSON').first().json.missing_fields.join(', ') }}",
"file_name": "={{ $('Parse Expense JSON').first().json.file_name }}",
"mime_type": "={{ $('Parse Expense JSON').first().json.mime_type }}",
"status": "READY_FOR_REVIEW",
"processed_at": "={{ $('Parse Expense JSON').first().json.processed_at }}",
"drive_url": "={{ $('Upload Receipt').first().json.webViewLink }}"
},
"matchingColumns": [],
"schema": [
{
"id": "expense_id",
"displayName": "expense_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "employee_email",
"displayName": "employee_email",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "merchant_name",
"displayName": "merchant_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "expense_date",
"displayName": "expense_date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "total_amount",
"displayName": "total_amount",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "currency",
"displayName": "currency",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "tax_amount",
"displayName": "tax_amount",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "category",
"displayName": "category",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "payment_method",
"displayName": "payment_method",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "document_number",
"displayName": "document_number",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "confidence",
"displayName": "confidence",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "missing_fields",
"displayName": "missing_fields",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "file_name",
"displayName": "file_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "mime_type",
"displayName": "mime_type",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "drive_url",
"displayName": "drive_url",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "processed_at",
"displayName": "processed_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
2880,
-240
],
"id": "cdf2ad93-8823-4b75-93eb-ddd82342f7c7",
"name": "Append Expense Row"
},
{
"parameters": {
"sendTo": "={{ $('Parse Expense JSON').first().json.employee_email }}",
"subject": "=Expense Received - {{ $('Parse Expense JSON').first().json.expense_id }}",
"message": "=Bonjour, Votre note de frais a bien \u00e9t\u00e9 re\u00e7ue et enregistr\u00e9e. Commer\u00e7ant : {{ $('Parse Expense JSON').first().json.merchant_name }} Montant : {{ $('Parse Expense JSON').first().json.total_amount }} {{ $('Parse Expense JSON').first().json.currency }} Date : {{ $('Parse Expense JSON').first().json.expense_date }} Cat\u00e9gorie : {{ $('Parse Expense JSON').first().json.category }} Statut : READY_FOR_REVIEW Votre document a \u00e9galement \u00e9t\u00e9 archiv\u00e9 dans Google Drive. R\u00e9f\u00e9rence : {{ $('Parse Expense JSON').first().json.expense_id }} Cordialement, AI Expense Tracker",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
3072,
-176
],
"id": "cf4f8e19-dec6-4ea4-9b11-3689cda19a6e",
"name": "send"
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "278be816-329f-4585-8d61-a21cea5c7ec8",
"leftValue": "={{ $json.confidence >= 0.85 && $json.total_amount != null && $json.total_amount > 0 }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "READY_FOR_REVIEW"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "9ae473c5-e45c-4ce3-a945-0c9d0780b820",
"leftValue": "={{ $json.confidence >= 0.60 && $json.confidence < 0.85 && $json.total_amount != null && $json.total_amount > 0 }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "NEEDS_REVIEW"
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "cd9fb99b-b3c3-493f-84ba-679dd5dab8cc",
"leftValue": "={{ $json.confidence < 0.60 || $json.total_amount == null || $json.total_amount <= 0 }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"renameOutput": true,
"outputKey": "REJECTED_EXTRACTION"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.switch",
"typeVersion": 3.4,
"position": [
2128,
-16
],
"id": "94a075b1-55db-44ba-9098-3fb423d599a5",
"name": "Route by Confidence"
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst sourceItem = $('Normalize Email & Attachment').first();\n\nreturn items.map(item => ({\n json: item.json,\n binary: sourceItem.binary\n}));"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2464,
-64
],
"id": "a297e4d7-2587-465b-98b8-f1750dff2e63",
"name": "READY_FOR_REVIEW"
},
{
"parameters": {
"inputDataFieldName": "={{ $('Normalize Email & Attachment').first().json.binary_property }}",
"name": "={{ $json.file_name }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive"
},
"folderId": {
"__rl": true,
"value": "YOUR_REVIEW_FOLDER_ID",
"mode": "id",
"cachedResultName": "Select your review folder"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [
2640,
-48
],
"id": "1e8d142d-dc96-43bf-a246-c9d02a6c467a",
"name": "Upload Receipt for Review"
},
{
"parameters": {
"sendTo": "={{ $('Parse Expense JSON').first().json.employee_email }}",
"subject": "=[Expense Review Required] {{ $('Parse Expense JSON').first().json.expense_id }}",
"message": "=Une note de frais n\u00e9cessite une v\u00e9rification humaine. R\u00e9f\u00e9rence : {{ $('Parse Expense JSON').first().json.expense_id }} Employ\u00e9 : {{ $('Parse Expense JSON').first().json.employee_email }} Commer\u00e7ant : {{ $('Parse Expense JSON').first().json.merchant_name }} Montant : {{ $('Parse Expense JSON').first().json.total_amount }} {{ $('Parse Expense JSON').first().json.currency }} Confiance IA : {{ $('Parse Expense JSON').first().json.confidence }} Champs manquants : {{ $('Parse Expense JSON').first().json.missing_fields.join(', ') }} Statut : NEEDS_REVIEW Veuillez v\u00e9rifier le document original archiv\u00e9 dans Google Drive.",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
3088,
0
],
"id": "4cbec0c1-4c74-4dd5-9354-9a82e24bc86d",
"name": "Send Review Alert"
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_REVIEW_SPREADSHEET_ID",
"mode": "id",
"cachedResultName": "Select your review spreadsheet"
},
"sheetName": {
"__rl": true,
"value": "YOUR_REVIEW_SHEET_ID",
"mode": "id",
"cachedResultName": "Select your review sheet"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"expense_id": "={{ $('Parse Expense JSON').first().json.expense_id }}",
"employee_email": "={{ $('Parse Expense JSON').first().json.employee_email }}",
"file_name": "={{ $('Parse Expense JSON').first().json.file_name }}",
"merchant_name": "={{ $('Parse Expense JSON').first().json.merchant_name }}",
"expense_date": "={{ $('Parse Expense JSON').first().json.expense_date }}",
"total_amount": "={{ $('Parse Expense JSON').first().json.total_amount }}",
"currency": "={{ $('Parse Expense JSON').first().json.currency }}",
"confidence": "={{ $('Parse Expense JSON').first().json.confidence }}",
"missing_fields": "={{ $('Parse Expense JSON').first().json.missing_fields.join(', ') }}",
"review_reason": "Low extraction confidence",
"status": "NEEDS_REVIEW",
"processed_at": "={{ $('Parse Expense JSON').first().json.processed_at }}",
"drive_url": "={{ $('Upload Receipt for Review').first().json.webViewLink }}"
},
"matchingColumns": [],
"schema": [
{
"id": "expense_id",
"displayName": "expense_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "employee_email",
"displayName": "employee_email",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "file_name",
"displayName": "file_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "merchant_name",
"displayName": "merchant_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "expense_date",
"displayName": "expense_date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "total_amount",
"displayName": "total_amount",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "currency",
"displayName": "currency",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "confidence",
"displayName": "confidence",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "missing_fields",
"displayName": "missing_fields",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "review_reason",
"displayName": "review_reason",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "drive_url",
"displayName": "drive_url",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "processed_at",
"displayName": "processed_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
2832,
-48
],
"id": "6cc5046c-4b09-4e4b-a182-628e0f919c20",
"name": "Append Review Queue"
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_REVIEW_SPREADSHEET_ID",
"mode": "id",
"cachedResultName": "Select your review spreadsheet"
},
"sheetName": {
"__rl": true,
"value": "YOUR_REVIEW_SHEET_ID",
"mode": "id",
"cachedResultName": "Select your review sheet"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"expense_id": "={{ $json.expense_id }}",
"employee_email": "={{ $json.employee_email }}",
"file_name": "={{ $json.file_name }}",
"merchant_name": "={{ $json.merchant_name }}",
"expense_date": "={{ $json.expense_date }}",
"total_amount": "={{ $json.total_amount }}",
"currency": "={{ $json.currency }}",
"confidence": "={{ $json.confidence }}",
"missing_fields": "={{ $json.missing_fields.join(', ') }}",
"review_reason": "Extraction rejected: confidence too low or amount missing/invalid",
"status": "REJECTED_EXTRACTION",
"processed_at": "={{ $json.processed_at }}"
},
"matchingColumns": [],
"schema": [
{
"id": "expense_id",
"displayName": "expense_id",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "employee_email",
"displayName": "employee_email",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "file_name",
"displayName": "file_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "merchant_name",
"displayName": "merchant_name",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "expense_date",
"displayName": "expense_date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "total_amount",
"displayName": "total_amount",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "currency",
"displayName": "currency",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "confidence",
"displayName": "confidence",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "missing_fields",
"displayName": "missing_fields",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "review_reason",
"displayName": "review_reason",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "drive_url",
"displayName": "drive_url",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "status",
"displayName": "status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "processed_at",
"displayName": "processed_at",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.7,
"position": [
2576,
160
],
"id": "8c26d0f8-dfdb-4062-9034-d868216877b2",
"name": "Log Rejected Expense"
},
{
"parameters": {
"sendTo": "={{ $json.employee_email }}",
"subject": "=Expense Could Not Be Processed - {{ $json.expense_id }}",
"message": "=Bonjour, Votre note de frais n'a pas pu \u00eatre trait\u00e9e automatiquement. R\u00e9f\u00e9rence : {{ $json.expense_id }} Fichier : {{ $json.file_name }} Confiance de l'extraction : {{ $json.confidence }} Champs manquants : {{ $json.missing_fields.join(', ') }} Statut : REJECTED_EXTRACTION Le document peut \u00eatre illisible ou certaines informations essentielles, notamment le montant total, peuvent \u00eatre absentes. Veuillez v\u00e9rifier le document et envoyer une nouvelle copie plus lisible. Cordialement, AI Expense Tracker",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
3088,
208
],
"id": "5460ec09-f2e2-44c1-b0dd-3fad7224dffd",
"name": "Send Rejection Alert"
},
{
"parameters": {
"sendTo": "={{ $json.employee_email }}",
"subject": "Expense Rejected - Unsupported File Type",
"message": "Bonjour, Votre note de frais n'a pas pu \u00eatre trait\u00e9e car le format du fichier envoy\u00e9 n'est pas accept\u00e9. Statut : UNSUPPORTED_FILE Formats accept\u00e9s : - PDF - JPG - PNG Veuillez convertir votre document dans l'un de ces formats et le renvoyer. Cordialement, AI Expense Tracker",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
624,
-32
],
"id": "19276c2a-bfd4-465a-928b-d0e8ca2c1921",
"name": "Send Unsupported File Alert"
},
{
"parameters": {
"sendTo": "={{ $json.employee_email }}",
"subject": "Expense Rejected - No Attachment",
"message": "Bonjour, Nous avons re\u00e7u votre demande de note de frais, mais aucune pi\u00e8ce jointe n'a \u00e9t\u00e9 d\u00e9tect\u00e9e. Statut : NO_ATTACHMENT Veuillez renvoyer votre demande avec votre re\u00e7u ou votre facture en pi\u00e8ce jointe. Formats accept\u00e9s : - PDF - JPG - PNG Cordialement, AI Expense Tracker",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
512,
176
],
"id": "80c2b968-fa8b-491b-b431-8bfe5449ba8b",
"name": "Send No Attachment Alert"
},
{
"parameters": {
"content": "\ud83d\udce9 Email Intake\n\nPurpose\nReceive new expense emails and validate incoming attachments before processing.\n\nResponsibilities\n\u2022 Monitor Gmail inbox\n\u2022 Normalize email metadata\n\u2022 Verify attachment presence\n\u2022 Validate supported file types (PDF, JPG, PNG)\n\nOutputs\n\u2022 Valid document \u2192 Processing\n\u2022 Missing attachment \u2192 Notification\n\u2022 Unsupported format \u2192 Notification",
"height": 784,
"width": 1088
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-80,
-400
],
"id": "f92d43d4-ddfa-4f3d-9978-531ee54a969c",
"name": "Sticky Note"
},
{
"parameters": {
"content": "\ud83d\udcc4 Document Processing\n\nPurpose\nExtract readable text from uploaded receipts.\n\nProcessing\n\u2022 PDF \u2192 PDF Text Extraction\n\u2022 JPG / PNG \u2192 OpenAI Vision OCR\n\nOutput\nClean text ready for AI analysis.",
"height": 976,
"width": 512,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
880,
-528
],
"id": "fcbe193f-af4a-4aa6-b644-1ff18c39f7cd",
"name": "Sticky Note1"
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/responses",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer YOUR_OPENAI_API_KEY"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"model\": \"gpt-4.1-mini\",\n \"input\": [\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"input_text\",\n \"text\": \"Lis cette image de re\u00e7u ou de facture. Extrais tout le texte visible sans r\u00e9sumer et sans inventer. Conserve pr\u00e9cis\u00e9ment le nom du commer\u00e7ant, la date, le montant total, la devise, la taxe, le moyen de paiement et le num\u00e9ro du document. Retourne uniquement le texte brut.\"\n },\n {\n \"type\": \"input_image\",\n \"image_url\": \"{{ 'data:' + $('Normalize Email & Attachment').item.json.mime_type + ';base64,' + $json.data }}\"\n }\n ]\n }\n ]\n}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.4,
"position": [
1248,
-112
],
"id": "b9a53330-3a9d-4f7e-83b9-d916471d8625",
"name": "OpenAI Vision OCR"
},
{
"parameters": {
"content": "\ud83e\udd16 AI Expense Extraction\n\nPurpose\nConvert receipt text into structured expense data.\n\nExtracted Fields\n\u2022 Merchant\n\u2022 Expense Date\n\u2022 Total Amount\n\u2022 Tax Amount\n\u2022 Currency\n\u2022 Category\n\u2022 Payment Method\n\u2022 Document Number\n\nOutput\nStructured JSON + Confidence Score",
"height": 912,
"width": 688,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1392,
-528
],
"id": "d54895c7-89e7-4000-bc3a-278eedec42a3",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "\ud83d\udcca Decision Engine\n\nBusiness Rules\n\n\ud83d\udfe2 Confidence \u2265 0.85\n\u2192 READY_FOR_REVIEW\n\n\ud83d\udfe1 Confidence 0.60\u20130.84\n\u2192 NEEDS_REVIEW\n\n\ud83d\udd34 Confidence < 0.60\n\u2192 REJECTED_EXTRACTION",
"height": 928,
"width": 320,
"color": "#63A711"
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
2096,
-528
],
"id": "3fe2e6ac-da1a-4587-ba8f-865f2d7c59e2",
"name": "Sticky Note3"
},
{
"parameters": {
"content": "\u2601\ufe0f Storage & Logging\n\nPurpose\nStore both documents and structured data.\n\nGoogle Drive\n\u2022 Archive original receipt\n\nGoogle Sheets\n\u2022 Save extracted expense\n\u2022 Log review queue\n\u2022 Log rejected receipts",
"height": 1040,
"width": 576,
"color": "#A92314"
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
2416,
-544
],
"id": "f7a20284-4bb6-477f-a5e3-33fd80f3208a",
"name": "Sticky Note4"
},
{
"parameters": {
"content": "\ud83d\udce7 Notifications\n\nAutomatic Emails\n\n\u2713 Expense Confirmed\n\n\u2713 Review Required\n\n\u2713 Extraction Rejected\n\n\u2713 Unsupported File\n\n\u2713 Missing Attachment",
"height": 912,
"width": 320
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
2992,
-544
],
"id": "8a367c78-4b94-4fa7-8e7b-1b10f6dcdb62",
"name": "Sticky Note5"
},
{
"parameters": {
"content": "\ud83d\ude80 AI Expense Tracker\n\nVersion: 1.0\n\nAn end-to-end AI-powered workflow that automates expense receipt processing.\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\nMain Features\n\n\u2713 Gmail Integration\n\u2713 PDF & Image Processing\n\u2713 OpenAI Vision OCR\n\u2713 AI Data Extraction\n\u2713 Confidence-Based Routing\n\u2713 Google Drive Storage\n\u2713 Google Sheets Logging\n\u2713 Automated Email Notifications\n\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\nSupported Files\n\n\u2022 PDF\n\u2022 JPG\n\u2022 PNG",
"height": 672,
"width": 1040,
"color": "#04DCC3"
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-80,
-1072
],
"id": "6067fb6c-0cde-4b71-9269-fb9318a0c0d4",
"name": "Sticky Note6"
}
],
"connections": {
"Receive Expense Email": {
"main": [
[
{
"node": "Normalize Email & Attachment",
"type": "main",
"index": 0
}
]
]
},
"Normalize Email & Attachment": {
"main": [
[
{
"node": "Has Attachment?",
"type": "main",
"index": 0
}
]
]
},
"Has Attachment?": {
"main": [
[
{
"node": "Validate File Type",
"type": "main",
"index": 0
}
],
[
{
"node": "Send No Attachment Alert",
"type": "main",
"index": 0
}
]
]
},
"Validate File Type": {
"main": [
[
{
"node": "Route by File Type",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Unsupported File Alert",
"type": "main",
"index": 0
}
]
]
},
"Route by File Type": {
"main": [
[
{
"node": "Extract Text from Receipt",
"type": "main",
"index": 0
}
],
[
{
"node": "Extract from File",
"type": "main",
"index": 0
}
],
[
{
"node": "Extract from File",
"type": "main",
"index": 0
}
]
]
},
"Extract from File": {
"main": [
[
{
"node": "OpenAI Vision OCR",
"type": "main",
"index": 0
}
]
]
},
"Extract Text from Receipt": {
"main": [
[
{
"node": "Prepare Expense Data",
"type": "main",
"index": 0
}
]
]
},
"Prepare Expense Data": {
"main": [
[
{
"node": "AI Expense Extractor",
"type": "main",
"index": 0
}
]
]
},
"AI Expense Extractor": {
"main": [
[
{
"node": "Parse Expense JSON",
"type": "main",
"index": 0
}
]
]
},
"Parse Expense JSON": {
"main": [
[
{
"node": "Route by Confidence",
"type": "main",
"index": 0
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Upload Receipt",
"type": "main",
"index": 0
}
]
]
},
"Upload Receipt": {
"main": [
[
{
"node": "Append Expense Row",
"type": "main",
"index": 0
}
]
]
},
"Append Expense Row": {
"main": [
[
{
"node": "send",
"type": "main",
"index": 0
}
]
]
},
"Route by Confidence": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
],
[
{
"node": "READY_FOR_REVIEW",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Rejected Expense",
"type": "main",
"index": 0
}
]
]
},
"READY_FOR_REVIEW": {
"main": [
[
{
"node": "Upload Receipt for Review",
"type": "main",
"index": 0
}
]
]
},
"Upload Receipt for Review": {
"main": [
[
{
"node": "Append Review Queue",
"type": "main",
"index": 0
}
]
]
},
"Append Review Queue": {
"main": [
[
{
"node": "Send Review Alert",
"type": "main",
"index": 0
}
]
]
},
"Log Rejected Expense": {
"main": [
[
{
"node": "Send Rejection Alert",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Vision OCR": {
"main": [
[
{
"node": "Prepare Expense Data",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": false
},
"nodeGroups": [],
"tags": []
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
AI Expense Tracker. Uses gmailTrigger, openAi, googleDrive, googleSheets. Event-driven trigger; 31 nodes.
Source: https://github.com/1liasx/ai-expense-tracker-n8n/blob/main/workflow/AI-Expense-Tracker.public.json — 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.
Overview
Small teams, solo operators, and security-conscious individuals who receive email attachments from external senders. Useful for freelancers, agencies, HR teams, and anyone handling CVs, invoices, or d
This workflow converts emailed timesheets into structured invoice rows in Google Sheets and stores them in the correct Google Drive folder structure.
This n8n workflow — HRMate — streamlines your entire recruitment process by automatically parsing incoming job applications, evaluating candidate fit using AI, and sending personalized acceptance or r
💥 Automate YouTube thumbnail creation from video links -vide. Uses telegramTrigger, httpRequest, googleDrive, gmail. Event-driven trigger; 25 nodes.