This workflow corresponds to n8n.io template #9600 — we link there as the canonical source.
This workflow follows the Gmail → 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": true
},
"nodes": [
{
"id": "0798c2b1-7ed5-45ce-a26c-2e8f7ada6a43",
"name": "Parse Form Data",
"type": "n8n-nodes-base.code",
"position": [
-864,
-16
],
"parameters": {
"jsCode": "// Parse form data and prepare for processing\nconst formData = $input.first().json;\n\nreturn {\n json: {\n submissionId: formData.submissionID || Date.now().toString(),\n employeeName: formData.employeeName || formData.q3_employeeName,\n employeeEmail: formData.employeeEmail || formData.q4_employeeEmail,\n employeeId: formData.employeeId || formData.q5_employeeId,\n amount: parseFloat(formData.amount || formData.q6_amount),\n category: formData.category || formData.q7_category,\n merchant: formData.merchant || formData.q8_merchant,\n date: formData.date || formData.q9_date,\n description: formData.description || formData.q10_description,\n receiptUrl: formData.receiptUrl || formData.q11_receipt,\n submittedAt: new Date().toISOString()\n }\n};"
},
"typeVersion": 2
},
{
"id": "df2085e9-dc08-4f25-a99e-009e2340b47c",
"name": "Validate Policy",
"type": "n8n-nodes-base.code",
"position": [
-672,
-16
],
"parameters": {
"jsCode": "// Company policy rules and validation\nconst data = $input.first().json;\n\nconst policy = {\n maxAmounts: {\n meals: 75,\n travel: 500,\n office_supplies: 200,\n software: 1000,\n entertainment: 150,\n other: 100\n },\n approvedCategories: ['meals', 'travel', 'office_supplies', 'software', 'entertainment'],\n autoApproveThreshold: 100,\n managerApprovalThreshold: 500\n};\n\nconst amount = data.amount;\nconst category = data.category;\n\nlet policyViolations = [];\nlet requiresApproval = 'auto';\n\nif (!policy.approvedCategories.includes(category)) {\n policyViolations.push('Category not approved');\n}\n\nif (policy.maxAmounts[category] && amount > policy.maxAmounts[category]) {\n policyViolations.push('Amount exceeds category limit');\n}\n\nif (amount < policy.autoApproveThreshold && policyViolations.length === 0) {\n requiresApproval = 'auto';\n} else if (amount >= policy.autoApproveThreshold && amount < policy.managerApprovalThreshold) {\n requiresApproval = 'manager';\n} else if (amount >= policy.managerApprovalThreshold) {\n requiresApproval = 'director';\n}\n\nreturn {\n json: {\n ...data,\n policyViolations: policyViolations,\n isCompliant: policyViolations.length === 0,\n requiresApproval: requiresApproval,\n status: 'pending'\n }\n};"
},
"typeVersion": 2
},
{
"id": "4ac52b34-7a64-49f5-b368-43d8b99bcab3",
"name": "Check Violations",
"type": "n8n-nodes-base.if",
"position": [
-464,
-16
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition1",
"operator": {
"type": "number",
"operation": "equals"
},
"leftValue": "={{ $json.policyViolations.length }}",
"rightValue": 0
}
]
}
},
"typeVersion": 2
},
{
"id": "9d132c2f-7f99-4b36-b543-27131702460c",
"name": "Set Rejection",
"type": "n8n-nodes-base.set",
"position": [
-272,
144
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "status",
"name": "status",
"type": "string",
"value": "rejected"
},
{
"id": "rejectionReason",
"name": "rejectionReason",
"type": "string",
"value": "=Policy violations: {{ $json.policyViolations.join(', ') }}"
}
]
}
},
"typeVersion": 3.3
},
{
"id": "47566677-5342-4b26-9974-793526a59bd9",
"name": "Route Auto",
"type": "n8n-nodes-base.if",
"position": [
-272,
-176
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.requiresApproval }}",
"rightValue": "auto"
}
]
}
},
"typeVersion": 2
},
{
"id": "c0b971bc-a5ce-48b6-978c-54ae02112505",
"name": "Auto Approve",
"type": "n8n-nodes-base.set",
"position": [
-64,
-272
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "status",
"name": "status",
"type": "string",
"value": "approved"
},
{
"id": "approvedBy",
"name": "approvedBy",
"type": "string",
"value": "System (Auto-Approved)"
},
{
"id": "approvedAt",
"name": "approvedAt",
"type": "string",
"value": "={{ new Date().toISOString() }}"
}
]
}
},
"typeVersion": 3.3
},
{
"id": "eef387ab-bee8-4570-8cca-26ccbd30abeb",
"name": "Route Manager",
"type": "n8n-nodes-base.if",
"position": [
-64,
-80
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.requiresApproval }}",
"rightValue": "manager"
}
]
}
},
"typeVersion": 2
},
{
"id": "74152681-2cf6-4e76-aabb-87ae00a86048",
"name": "Slack Manager",
"type": "n8n-nodes-base.slack",
"position": [
144,
-224
],
"parameters": {
"text": "=New Expense Approval Required\nEmployee: {{ $json.employeeName }}\nAmount: ${{ $json.amount }}\nCategory: {{ $json.category }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C01234567"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "e05fba8b-60e5-4438-9f7a-cd828bace8b8",
"name": "Slack Director",
"type": "n8n-nodes-base.slack",
"position": [
144,
-16
],
"parameters": {
"text": "=High-Value Expense - Director Approval\nEmployee: {{ $json.employeeName }}\nAmount: ${{ $json.amount }}\nCategory: {{ $json.category }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C98765432"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "b7a58104-fc21-485a-8c93-edb1dc253b2b",
"name": "Log to Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
336,
-64
],
"parameters": {
"columns": {
"value": {},
"schema": [],
"mappingMode": "autoMapInputData",
"matchingColumns": [
"submissionId"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_GOOGLE_SHEET_ID"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "1af29bbe-b3ee-42ee-91a9-388b70659c48",
"name": "Is Approved",
"type": "n8n-nodes-base.if",
"position": [
544,
-64
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition1",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "approved"
}
]
}
},
"typeVersion": 2
},
{
"id": "0e8c5d12-fee3-4c7d-bc16-77a79e39befe",
"name": "Rejection Email",
"type": "n8n-nodes-base.gmail",
"position": [
752,
0
],
"parameters": {
"sendTo": "={{ $json.employeeEmail }}",
"message": "=Hi, Your expense for {{ $json.amount}} has been rejected as it did not matches with the company's validation policy.",
"options": {},
"subject": "=Expense Rejected - ${{ $json.amount }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "1b5d88c6-fe8f-4984-9e9e-19789ba76c60",
"name": "Approved",
"type": "n8n-nodes-base.gmail",
"position": [
752,
-192
],
"parameters": {
"sendTo": "={{ $json.employeeEmail }}",
"message": "Hi, Your expense for {{ $json.Amount }} has been approved",
"options": {},
"subject": "=Expense Approved - ${{ $json.amount }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "6382a19b-08de-4466-9013-696d5ffac4db",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1200,
-208
],
"parameters": {
"height": 432,
"content": "\ud83d\udce9 Trigger: New Expense Submission\nCaptures employee expense details \nand receipt upload from Jotform.\nCreate your form for free on [Jotform using this link](https://www.jotform.com/?partner=mediajade) \n"
},
"typeVersion": 1
},
{
"id": "5b890f09-277e-41e6-9628-dd6907b14e5f",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-912,
-160
],
"parameters": {
"width": 192,
"content": "\ud83e\uddfe Parse & Normalize Data\nExtracts key fields (name, email, amount, \ncategory, merchant, receipt URL) \nfor further processing.\n"
},
"typeVersion": 1
},
{
"id": "fe3ea799-b6f3-48f4-bbc4-96623e1e8256",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-720,
112
],
"parameters": {
"width": 208,
"height": 192,
"content": "\u2699\ufe0f Policy Validation\nChecks compliance against company rules:\n- Allowed categories\n- Max amount limits\n- Auto/Manager/Director approval routing\n"
},
"typeVersion": 1
},
{
"id": "11112b1f-af67-4c7c-86cc-b4ccfe83a249",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-512,
-160
],
"parameters": {
"width": 176,
"content": "\ud83d\udeab Compliance Check\nIf violations exist \u2192 reject\nElse \u2192 continue for approval routing\n"
},
"typeVersion": 1
},
{
"id": "054e826f-590f-4de0-8ccd-d299d2f6e044",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-288,
-368
],
"parameters": {
"width": 576,
"height": 496,
"content": "\u26a1 Routing Decision\nDetermines if expense should be:\n- Auto-approved\n- Sent for manager approval\n- Sent for director approval\n"
},
"typeVersion": 1
},
{
"id": "6ebce30b-f8d7-4bf2-bdf3-6f33b8363a55",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
320,
-224
],
"parameters": {
"width": 208,
"height": 384,
"content": "\ud83d\udcca Audit Logging\nAppends expense status, \napprover details, and decision \nto Google Sheets for tracking.\n"
},
"typeVersion": 1
},
{
"id": "f5e44753-7705-41e1-bdac-0158deea26ab",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
-384
],
"parameters": {
"width": 384,
"height": 576,
"content": "\ud83d\udd0d Approval Check\nBranches workflow to send \napproval or rejection emails \nbased on final status.\n"
},
"typeVersion": 1
},
{
"id": "2eb6d9f2-7f9d-453f-a548-cf2660f7248c",
"name": "Jotform Trigger",
"type": "n8n-nodes-base.jotFormTrigger",
"position": [
-1120,
-16
],
"parameters": {
"form": "252860582136459"
},
"credentials": {
"jotFormApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
}
],
"connections": {
"Route Auto": {
"main": [
[
{
"node": "Auto Approve",
"type": "main",
"index": 0
}
],
[
{
"node": "Route Manager",
"type": "main",
"index": 0
}
]
]
},
"Is Approved": {
"main": [
[
{
"node": "Approved",
"type": "main",
"index": 0
}
],
[
{
"node": "Rejection Email",
"type": "main",
"index": 0
}
]
]
},
"Auto Approve": {
"main": [
[
{
"node": "Log to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Log to Sheets": {
"main": [
[
{
"node": "Is Approved",
"type": "main",
"index": 0
}
]
]
},
"Route Manager": {
"main": [
[
{
"node": "Slack Manager",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack Director",
"type": "main",
"index": 0
}
]
]
},
"Set Rejection": {
"main": [
[
{
"node": "Log to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Slack Manager": {
"main": [
[
{
"node": "Log to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Slack Director": {
"main": [
[
{
"node": "Log to Sheets",
"type": "main",
"index": 0
}
]
]
},
"Jotform Trigger": {
"main": [
[
{
"node": "Parse Form Data",
"type": "main",
"index": 0
}
]
]
},
"Parse Form Data": {
"main": [
[
{
"node": "Validate Policy",
"type": "main",
"index": 0
}
]
]
},
"Validate Policy": {
"main": [
[
{
"node": "Check Violations",
"type": "main",
"index": 0
}
]
]
},
"Check Violations": {
"main": [
[
{
"node": "Route Auto",
"type": "main",
"index": 0
}
],
[
{
"node": "Set Rejection",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
gmailOAuth2googleSheetsOAuth2ApijotFormApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Eliminate weeks of waiting and mountains of paperwork with intelligent expense automation that processes reimbursements in 72 hours instead of 2–3 weeks — delivering 90% reduction in manual processing time.
Source: https://n8n.io/workflows/9600/ — 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.
Categories: Payments, Project Operations, Client Onboarding
Sync your Google Calendar events with Google Sheets and get daily Slack summaries with meeting statistics. FEATURES:
Stop chasing blurry receipts and manually typing expense data. This workflow creates an intelligent, "snap-and-submit" reimbursement pipeline that hosts photos via UploadToURL, extracts deep data via
This template automates internal equipment and supply purchase requests for operations, HR, and IT teams. Requests are submitted via a built-in n8n form, automatically approved for small amounts, and
Automate daily KPI tracking and reporting by integrating ClickUp tasks and Google Sheets lead data into a unified dashboard. This workflow computes performance metrics, analyzes sentiment, and deliver