This workflow corresponds to n8n.io template #8728 — we link there as the canonical source.
This workflow follows the Chainllm → Form 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 →
{
"id": "rtK6UJNlclMQboZu",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Route equipment requests to Procurement with manager approval via Gmail",
"tags": [],
"nodes": [
{
"id": "37da6182-7899-43ea-8266-f63209c78a45",
"name": "Question form",
"type": "n8n-nodes-base.formTrigger",
"position": [
-416,
-144
],
"parameters": {
"options": {
"customCss": "/* ===== Base layout ===== */\nhtml, body {\n height: 100%;\n font-family: var(--font-family);\n font-weight: var(--font-weight-normal);\n font-size: var(--font-size-body);\n background:\n radial-gradient(1200px 800px at 10% -10%, #f0f2ff 0%, transparent 60%),\n radial-gradient(1000px 700px at 110% 0%, #fef3f0 0%, transparent 55%),\n var(--color-background);\n color: var(--color-html-text);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n.container, .form-wrapper, .n8n-form {\n max-width: var(--container-width);\n margin: 32px auto;\n padding: 0 12px;\n}\n\n/* ===== Test banner (top) ===== */\n.test-notice {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 auto 12px;\n max-width: var(--container-width);\n background: var(--color-test-notice-bg);\n border: 1px solid var(--color-test-notice-border);\n color: var(--color-test-notice-text);\n padding: var(--padding-test-notice-vertical) var(--padding-test-notice-horizontal);\n border-radius: var(--border-radius-input);\n font-size: var(--font-size-test-notice);\n}\n\n/* ===== Card ===== */\n.form-card {\n background: var(--color-card-bg);\n border: 1px solid var(--color-card-border);\n border-radius: var(--border-radius-card);\n box-shadow: var(--box-shadow-card);\n padding: var(--padding-card);\n margin-bottom: var(--margin-bottom-card);\n}\n\n/* ===== Headings & subtitle ===== */\n.form-card h1,\nh1.form-title {\n font-size: var(--font-size-header);\n color: var(--color-header);\n margin: 4px 0 6px;\n font-weight: var(--font-weight-bold);\n letter-spacing: -0.2px;\n}\n\n.form-subtitle {\n font-size: var(--font-size-paragraph);\n color: var(--color-header-subtext);\n margin: 0 0 16px;\n line-height: 1.5;\n}\n\n/* ===== Form fields ===== */\n.form-field {\n display: grid;\n gap: 6px;\n margin-bottom: 14px;\n}\n\n.form-field label {\n font-size: var(--font-size-label);\n color: var(--color-label);\n font-weight: 600;\n}\n\n.form-field label .required {\n color: var(--color-required);\n margin-left: 2px;\n}\n\n/* Inputs / selects / textareas */\ninput[type=\"text\"],\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ntextarea,\nselect {\n width: 100%;\n appearance: none;\n border: 1px solid var(--color-input-border);\n border-radius: var(--border-radius-input);\n padding: 12px 14px;\n font-size: var(--font-size-input);\n color: var(--color-input-text);\n background: #fff;\n transition: border-color .15s ease, box-shadow .15s ease, transform .05s ease;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: var(--opacity-placeholder);\n}\n\n/* Hover / focus */\ninput:hover,\ntextarea:hover,\nselect:hover {\n border-color: #cfd5e2;\n}\n\ninput:focus,\ntextarea:focus,\nselect:focus {\n outline: none;\n border-color: var(--color-focus-border);\n box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* Invalid */\n.is-invalid,\ninput[aria-invalid=\"true\"],\ntextarea[aria-invalid=\"true\"],\nselect[aria-invalid=\"true\"] {\n border-color: var(--color-error);\n box-shadow: 0 0 0 4px rgba(234, 31, 48, .12);\n}\n\n/* Help / error text */\n.help-text {\n font-size: 12px;\n color: var(--color-link);\n}\n\n.error-text {\n font-size: var(--font-size-error);\n color: var(--color-error);\n margin-top: 4px;\n}\n\n/* ===== File input (custom) ===== */\n.input-file {\n position: relative;\n}\n\n.input-file input[type=\"file\"] {\n position: absolute;\n inset: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n.input-file .file-ui {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n border: 1px dashed var(--color-input-border);\n border-radius: var(--border-radius-input);\n padding: 12px 14px;\n color: var(--color-input-text);\n background: #fff;\n transition: border-color .15s ease, box-shadow .15s ease;\n}\n\n.input-file .file-ui:hover {\n border-color: #cfd5e2;\n}\n\n.input-file .file-ui:has(+ input[type=\"file\"]:focus) {\n border-color: var(--color-focus-border);\n box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* ===== Checkboxes / radios (accessible but nicer) ===== */\ninput[type=\"checkbox\"], input[type=\"radio\"] {\n accent-color: var(--color-focus-border);\n width: var(--checkbox-size);\n height: var(--checkbox-size);\n}\n\n/* ===== Buttons ===== */\n.actions,\n.form-actions {\n margin-top: 8px;\n}\n\nbutton[type=\"submit\"],\n.button-submit {\n width: 100%;\n height: var(--submit-btn-height);\n background: var(--color-submit-btn-bg);\n color: var(--color-submit-btn-text);\n border: 1px solid transparent;\n border-radius: var(--border-radius-input);\n font-size: 15px;\n font-weight: 700;\n letter-spacing: .2px;\n cursor: pointer;\n transition: transform .05s ease, filter .15s ease, box-shadow .15s ease;\n box-shadow: 0 6px 18px rgba(255, 109, 90, .25);\n}\n\nbutton[type=\"submit\"]:hover {\n filter: brightness(0.98);\n transform: translateY(-1px);\n box-shadow: 0 8px 24px rgba(255, 109, 90, .28);\n}\n\nbutton[type=\"submit\"]:active {\n transform: translateY(0);\n box-shadow: 0 6px 18px rgba(255, 109, 90, .22);\n}\n\nbutton[disabled] {\n opacity: .6;\n cursor: not-allowed;\n}\n\n/* Secondary link under button (e.g., \u201cClear form\u201d) */\n.clear-link {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin-top: 10px;\n font-size: var(--font-size-link);\n color: var(--color-link);\n text-decoration: none;\n}\n.clear-link:hover { text-decoration: underline; }\n\n/* ===== Footer / \u201cAutomated with n8n\u201d ===== */\n.form-footer {\n margin: 10px auto 24px;\n max-width: var(--container-width);\n text-align: center;\n color: var(--color-link);\n font-size: var(--font-size-link);\n}\n\n/* ===== Responsive ===== */\n@media (max-width: 520px) {\n .container, .form-wrapper, .n8n-form {\n margin: 20px auto;\n padding: 0 10px;\n }\n .form-card { padding: 20px; }\n .form-card h1, h1.form-title { font-size: 18px; }\n .form-subtitle { font-size: 13px; }\n}\n\n/* ===== Optional: Dark mode (auto) ===== */\n@media (prefers-color-scheme: dark) {\n body {\n background:\n radial-gradient(1200px 800px at 10% -10%, #272a33 0%, transparent 60%),\n radial-gradient(1000px 700px at 110% 0%, #2c2433 0%, transparent 55%),\n #16181d;\n color: #d8dbe3;\n }\n\n .test-notice {\n background: #2a2115;\n border-color: #3a2b18;\n color: #ffce74;\n }\n\n .form-card {\n background: #1c1f27;\n border-color: #2a2f3a;\n box-shadow: 0 8px 24px rgba(0,0,0,.35);\n }\n\n .form-card h1, h1.form-title { color: #e9ebf3; }\n .form-subtitle { color: #a6adbb; }\n\n input, textarea, select {\n background: #11151c;\n border-color: #2a2f3a;\n color: #d8dbe3;\n }\n input:hover, textarea:hover, select:hover { border-color: #3a4250; }\n input:focus, textarea:focus, select:focus {\n border-color: #b7a8ff;\n box-shadow: 0 0 0 4px rgba(183,168,255,.18);\n }\n\n .help-text { color: #9aa3b2; }\n\n button[type=\"submit\"],\n .button-submit {\n box-shadow: 0 10px 26px rgba(255,109,90,.34);\n }\n\n .form-footer { color: #9aa3b2; }\n}\n",
"buttonLabel": "Send question"
},
"formTitle": "Equipment Request Form",
"formFields": {
"values": [
{
"fieldLabel": "Your employee enrollment",
"requiredField": true
}
]
},
"responseMode": "lastNode",
"formDescription": "Please fill in your employee ID so we can validate your information and identify your manager"
},
"typeVersion": 2.3
},
{
"id": "4e3062f4-9644-4830-9410-1bdf4802a49d",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
400,
-144
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d0fdfd93-0d28-43cf-8db7-1a4b22d57a15",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.isEmpty() }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "b6487e94-9186-4ec6-b0a6-cbe4fb04401c",
"name": "Return Employee not found",
"type": "n8n-nodes-base.form",
"position": [
720,
-352
],
"parameters": {
"options": {},
"operation": "completion",
"completionTitle": "Employee not found",
"completionMessage": "It was not possible to find your registration based on your enrollment number."
},
"typeVersion": 2.3
},
{
"id": "1f8c6236-784a-49e9-9da6-4ad8b53d7de5",
"name": "Get request information",
"type": "n8n-nodes-base.form",
"position": [
-416,
464
],
"parameters": {
"options": {
"customCss": "/* ===== Base layout ===== */\nhtml, body {\n height: 100%;\n font-family: var(--font-family);\n font-weight: var(--font-weight-normal);\n font-size: var(--font-size-body);\n background:\n radial-gradient(1200px 800px at 10% -10%, #f0f2ff 0%, transparent 60%),\n radial-gradient(1000px 700px at 110% 0%, #fef3f0 0%, transparent 55%),\n var(--color-background);\n color: var(--color-html-text);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n.container, .form-wrapper, .n8n-form {\n max-width: var(--container-width);\n margin: 32px auto;\n padding: 0 12px;\n}\n\n/* ===== Test banner (top) ===== */\n.test-notice {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 auto 12px;\n max-width: var(--container-width);\n background: var(--color-test-notice-bg);\n border: 1px solid var(--color-test-notice-border);\n color: var(--color-test-notice-text);\n padding: var(--padding-test-notice-vertical) var(--padding-test-notice-horizontal);\n border-radius: var(--border-radius-input);\n font-size: var(--font-size-test-notice);\n}\n\n/* ===== Card ===== */\n.form-card {\n background: var(--color-card-bg);\n border: 1px solid var(--color-card-border);\n border-radius: var(--border-radius-card);\n box-shadow: var(--box-shadow-card);\n padding: var(--padding-card);\n margin-bottom: var(--margin-bottom-card);\n}\n\n/* ===== Headings & subtitle ===== */\n.form-card h1,\nh1.form-title {\n font-size: var(--font-size-header);\n color: var(--color-header);\n margin: 4px 0 6px;\n font-weight: var(--font-weight-bold);\n letter-spacing: -0.2px;\n}\n\n.form-subtitle {\n font-size: var(--font-size-paragraph);\n color: var(--color-header-subtext);\n margin: 0 0 16px;\n line-height: 1.5;\n}\n\n/* ===== Form fields ===== */\n.form-field {\n display: grid;\n gap: 6px;\n margin-bottom: 14px;\n}\n\n.form-field label {\n font-size: var(--font-size-label);\n color: var(--color-label);\n font-weight: 600;\n}\n\n.form-field label .required {\n color: var(--color-required);\n margin-left: 2px;\n}\n\n/* Inputs / selects / textareas */\ninput[type=\"text\"],\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ntextarea,\nselect {\n width: 100%;\n appearance: none;\n border: 1px solid var(--color-input-border);\n border-radius: var(--border-radius-input);\n padding: 12px 14px;\n font-size: var(--font-size-input);\n color: var(--color-input-text);\n background: #fff;\n transition: border-color .15s ease, box-shadow .15s ease, transform .05s ease;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: var(--opacity-placeholder);\n}\n\n/* Hover / focus */\ninput:hover,\ntextarea:hover,\nselect:hover {\n border-color: #cfd5e2;\n}\n\ninput:focus,\ntextarea:focus,\nselect:focus {\n outline: none;\n border-color: var(--color-focus-border);\n box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* Invalid */\n.is-invalid,\ninput[aria-invalid=\"true\"],\ntextarea[aria-invalid=\"true\"],\nselect[aria-invalid=\"true\"] {\n border-color: var(--color-error);\n box-shadow: 0 0 0 4px rgba(234, 31, 48, .12);\n}\n\n/* Help / error text */\n.help-text {\n font-size: 12px;\n color: var(--color-link);\n}\n\n.error-text {\n font-size: var(--font-size-error);\n color: var(--color-error);\n margin-top: 4px;\n}\n\n/* ===== File input (custom) ===== */\n.input-file {\n position: relative;\n}\n\n.input-file input[type=\"file\"] {\n position: absolute;\n inset: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n.input-file .file-ui {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n border: 1px dashed var(--color-input-border);\n border-radius: var(--border-radius-input);\n padding: 12px 14px;\n color: var(--color-input-text);\n background: #fff;\n transition: border-color .15s ease, box-shadow .15s ease;\n}\n\n.input-file .file-ui:hover {\n border-color: #cfd5e2;\n}\n\n.input-file .file-ui:has(+ input[type=\"file\"]:focus) {\n border-color: var(--color-focus-border);\n box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* ===== Checkboxes / radios (accessible but nicer) ===== */\ninput[type=\"checkbox\"], input[type=\"radio\"] {\n accent-color: var(--color-focus-border);\n width: var(--checkbox-size);\n height: var(--checkbox-size);\n}\n\n/* ===== Buttons ===== */\n.actions,\n.form-actions {\n margin-top: 8px;\n}\n\nbutton[type=\"submit\"],\n.button-submit {\n width: 100%;\n height: var(--submit-btn-height);\n background: var(--color-submit-btn-bg);\n color: var(--color-submit-btn-text);\n border: 1px solid transparent;\n border-radius: var(--border-radius-input);\n font-size: 15px;\n font-weight: 700;\n letter-spacing: .2px;\n cursor: pointer;\n transition: transform .05s ease, filter .15s ease, box-shadow .15s ease;\n box-shadow: 0 6px 18px rgba(255, 109, 90, .25);\n}\n\nbutton[type=\"submit\"]:hover {\n filter: brightness(0.98);\n transform: translateY(-1px);\n box-shadow: 0 8px 24px rgba(255, 109, 90, .28);\n}\n\nbutton[type=\"submit\"]:active {\n transform: translateY(0);\n box-shadow: 0 6px 18px rgba(255, 109, 90, .22);\n}\n\nbutton[disabled] {\n opacity: .6;\n cursor: not-allowed;\n}\n\n/* Secondary link under button (e.g., \u201cClear form\u201d) */\n.clear-link {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin-top: 10px;\n font-size: var(--font-size-link);\n color: var(--color-link);\n text-decoration: none;\n}\n.clear-link:hover { text-decoration: underline; }\n\n/* ===== Footer / \u201cAutomated with n8n\u201d ===== */\n.form-footer {\n margin: 10px auto 24px;\n max-width: var(--container-width);\n text-align: center;\n color: var(--color-link);\n font-size: var(--font-size-link);\n}\n\n/* ===== Responsive ===== */\n@media (max-width: 520px) {\n .container, .form-wrapper, .n8n-form {\n margin: 20px auto;\n padding: 0 10px;\n }\n .form-card { padding: 20px; }\n .form-card h1, h1.form-title { font-size: 18px; }\n .form-subtitle { font-size: 13px; }\n}\n\n/* ===== Optional: Dark mode (auto) ===== */\n@media (prefers-color-scheme: dark) {\n body {\n background:\n radial-gradient(1200px 800px at 10% -10%, #272a33 0%, transparent 60%),\n radial-gradient(1000px 700px at 110% 0%, #2c2433 0%, transparent 55%),\n #16181d;\n color: #d8dbe3;\n }\n\n .test-notice {\n background: #2a2115;\n border-color: #3a2b18;\n color: #ffce74;\n }\n\n .form-card {\n background: #1c1f27;\n border-color: #2a2f3a;\n box-shadow: 0 8px 24px rgba(0,0,0,.35);\n }\n\n .form-card h1, h1.form-title { color: #e9ebf3; }\n .form-subtitle { color: #a6adbb; }\n\n input, textarea, select {\n background: #11151c;\n border-color: #2a2f3a;\n color: #d8dbe3;\n }\n input:hover, textarea:hover, select:hover { border-color: #3a4250; }\n input:focus, textarea:focus, select:focus {\n border-color: #b7a8ff;\n box-shadow: 0 0 0 4px rgba(183,168,255,.18);\n }\n\n .help-text { color: #9aa3b2; }\n\n button[type=\"submit\"],\n .button-submit {\n box-shadow: 0 10px 26px rgba(255,109,90,.34);\n }\n\n .form-footer { color: #9aa3b2; }\n}\n"
},
"formFields": {
"values": [
{
"fieldType": "textarea",
"fieldLabel": "What would you like to request?",
"requiredField": true
}
]
}
},
"typeVersion": 2.3
},
{
"id": "393cdcc8-af4d-42bb-b8c1-5891d1b089de",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
2304,
400
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-nano",
"cachedResultName": "gpt-4.1-nano"
},
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "fae1ff57-f79b-46e6-a9c1-97abab368585",
"name": "Get Employee",
"type": "n8n-nodes-base.mySql",
"position": [
16,
-144
],
"parameters": {
"table": {
"__rl": true,
"mode": "list",
"value": "employees",
"cachedResultName": "employees"
},
"where": {
"values": [
{
"value": "={{ $json['Your employee enrollment'] }}",
"column": "enrollment_number"
}
]
},
"options": {},
"operation": "select",
"returnAll": true
},
"credentials": {
"mySql": {
"name": "<your credential>"
}
},
"typeVersion": 2.5,
"alwaysOutputData": true
},
{
"id": "c5d3a970-c61e-4a87-910e-18809d9f7cee",
"name": "Employee data",
"type": "n8n-nodes-base.set",
"position": [
720,
96
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "3f65a01e-465e-412e-b9f5-31b366724dc7",
"name": "employee.enrollment_number",
"type": "string",
"value": "={{ $json.enrollment_number }}"
},
{
"id": "e2f24a33-b96b-4106-a7cc-bdad369407b4",
"name": "employee.name",
"type": "string",
"value": "={{ $json.name }}"
},
{
"id": "c7fe12b0-389d-477b-ab73-62da0fcafd3a",
"name": "employee.email",
"type": "string",
"value": "={{ $json.email }}"
},
{
"id": "8ceb93de-c37a-4c9e-af13-54a582a2e7bd",
"name": "employee.manager",
"type": "number",
"value": "={{ $json.manager }}"
},
{
"id": "971f3df4-3b51-44a0-b824-9c0d43f86f92",
"name": "employee.requires_approval",
"type": "boolean",
"value": "={{ $json.manager ? true : false }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "1858f338-3622-4d98-8d07-051069436f39",
"name": "Is approval required?",
"type": "n8n-nodes-base.if",
"position": [
1040,
48
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "16796776-0e35-4704-86fa-d65e91d9c20f",
"operator": {
"type": "number",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.employee.manager }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "c3e7dc19-8840-4108-8049-ad34aa0ef9f9",
"name": "Get Manager",
"type": "n8n-nodes-base.mySql",
"position": [
1360,
32
],
"parameters": {
"table": {
"__rl": true,
"mode": "list",
"value": "employees",
"cachedResultName": "employees"
},
"where": {
"values": [
{
"value": "={{ $json.employee.manager }}",
"column": "id"
}
]
},
"options": {},
"operation": "select",
"returnAll": true
},
"credentials": {
"mySql": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
},
{
"id": "d5df5874-e4e4-47c7-a224-120f2f69f0cc",
"name": "Employee and Manager",
"type": "n8n-nodes-base.merge",
"position": [
2016,
16
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "da2d6915-dcb3-4a11-a36c-a7e39ae5212b",
"name": "Set Manager field",
"type": "n8n-nodes-base.set",
"position": [
1680,
32
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "7b2ad1ea-22a6-4e66-b17b-12e6c8273a75",
"name": "manager",
"type": "object",
"value": "={{ $json }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "f85e302e-2285-43d8-a29d-8b11267cb442",
"name": "Merge Employee data and request",
"type": "n8n-nodes-base.merge",
"position": [
1360,
448
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "abb1fb4d-f473-41e8-aef2-9679dd56356c",
"name": "Approval request message",
"type": "n8n-nodes-base.if",
"position": [
1696,
448
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "586bed7e-b297-47ef-bdd0-d0e17de39ef7",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.employee.requires_approval }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "ba028c9d-b723-4bac-9713-7ab9fb98d32a",
"name": "Create approval message",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
2352,
256
],
"parameters": {
"text": "=Employee request:\n\n{{ $json['What would you like to request?'] }}\n",
"batching": {},
"messages": {
"messageValues": [
{
"message": "=You are a writing assistant. \nYou will receive an equipment/item request. \nYour tasks are: \n1. Sanitize the request (fix grammar, remove unnecessary words, strange characters, or informal language). \n2. Rewrite it into a clear, professional, and polite approval request message to a manager. \n3. Output the result as a valid JSON object containing both the improved request and the full e-mail text. \n\nKeep the tone formal, concise, and business-appropriate. \nDo not invent information; only improve grammar, clarity, and tone. \n\nFormat strictly as JSON, like this:\n\n{\n \"sanitized_request\": \"[sanitized and improved version of the request]\",\n \"email_text\": \"Dear {{ $json.manager.name }},\\n\\nPlease approve the procurement of [sanitized_request] for employee {{ $json.employee.name }} (Enrollment: {{ $json.employee.enrollment_number }}).\\n\\nThank you for your attention to this request.\\n\\nBest regards,\\nEquipment request AI\"\n}\n\nEmployee data:\nEnrollment: {{ $json.employee.enrollment_number }}\nName: {{ $json.employee.name }}\nEmail: {{ $json.employee.email }}\n\nManager data:\nName: {{ $json.manager.name }}\nEmail: {{ $json.manager.email }}\n"
}
]
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.7
},
{
"id": "8cffcddb-27f7-4f16-8e10-95302097c363",
"name": "Data and message",
"type": "n8n-nodes-base.merge",
"position": [
2816,
272
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "836480a0-a663-4bcb-9357-034b889e2170",
"name": "End employee request",
"type": "n8n-nodes-base.form",
"position": [
3248,
272
],
"parameters": {
"options": {
"customCss": "/* ===== Base layout ===== */\nhtml, body {\n height: 100%;\n font-family: var(--font-family);\n font-weight: var(--font-weight-normal);\n font-size: var(--font-size-body);\n background:\n radial-gradient(1200px 800px at 10% -10%, #f0f2ff 0%, transparent 60%),\n radial-gradient(1000px 700px at 110% 0%, #fef3f0 0%, transparent 55%),\n var(--color-background);\n color: var(--color-html-text);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n.container, .form-wrapper, .n8n-form {\n max-width: var(--container-width);\n margin: 32px auto;\n padding: 0 12px;\n}\n\n/* ===== Test banner (top) ===== */\n.test-notice {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 auto 12px;\n max-width: var(--container-width);\n background: var(--color-test-notice-bg);\n border: 1px solid var(--color-test-notice-border);\n color: var(--color-test-notice-text);\n padding: var(--padding-test-notice-vertical) var(--padding-test-notice-horizontal);\n border-radius: var(--border-radius-input);\n font-size: var(--font-size-test-notice);\n}\n\n/* ===== Card ===== */\n.form-card {\n background: var(--color-card-bg);\n border: 1px solid var(--color-card-border);\n border-radius: var(--border-radius-card);\n box-shadow: var(--box-shadow-card);\n padding: var(--padding-card);\n margin-bottom: var(--margin-bottom-card);\n}\n\n/* ===== Headings & subtitle ===== */\n.form-card h1,\nh1.form-title {\n font-size: var(--font-size-header);\n color: var(--color-header);\n margin: 4px 0 6px;\n font-weight: var(--font-weight-bold);\n letter-spacing: -0.2px;\n}\n\n.form-subtitle {\n font-size: var(--font-size-paragraph);\n color: var(--color-header-subtext);\n margin: 0 0 16px;\n line-height: 1.5;\n}\n\n/* ===== Form fields ===== */\n.form-field {\n display: grid;\n gap: 6px;\n margin-bottom: 14px;\n}\n\n.form-field label {\n font-size: var(--font-size-label);\n color: var(--color-label);\n font-weight: 600;\n}\n\n.form-field label .required {\n color: var(--color-required);\n margin-left: 2px;\n}\n\n/* Inputs / selects / textareas */\ninput[type=\"text\"],\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ntextarea,\nselect {\n width: 100%;\n appearance: none;\n border: 1px solid var(--color-input-border);\n border-radius: var(--border-radius-input);\n padding: 12px 14px;\n font-size: var(--font-size-input);\n color: var(--color-input-text);\n background: #fff;\n transition: border-color .15s ease, box-shadow .15s ease, transform .05s ease;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: var(--opacity-placeholder);\n}\n\n/* Hover / focus */\ninput:hover,\ntextarea:hover,\nselect:hover {\n border-color: #cfd5e2;\n}\n\ninput:focus,\ntextarea:focus,\nselect:focus {\n outline: none;\n border-color: var(--color-focus-border);\n box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* Invalid */\n.is-invalid,\ninput[aria-invalid=\"true\"],\ntextarea[aria-invalid=\"true\"],\nselect[aria-invalid=\"true\"] {\n border-color: var(--color-error);\n box-shadow: 0 0 0 4px rgba(234, 31, 48, .12);\n}\n\n/* Help / error text */\n.help-text {\n font-size: 12px;\n color: var(--color-link);\n}\n\n.error-text {\n font-size: var(--font-size-error);\n color: var(--color-error);\n margin-top: 4px;\n}\n\n/* ===== File input (custom) ===== */\n.input-file {\n position: relative;\n}\n\n.input-file input[type=\"file\"] {\n position: absolute;\n inset: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n.input-file .file-ui {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n border: 1px dashed var(--color-input-border);\n border-radius: var(--border-radius-input);\n padding: 12px 14px;\n color: var(--color-input-text);\n background: #fff;\n transition: border-color .15s ease, box-shadow .15s ease;\n}\n\n.input-file .file-ui:hover {\n border-color: #cfd5e2;\n}\n\n.input-file .file-ui:has(+ input[type=\"file\"]:focus) {\n border-color: var(--color-focus-border);\n box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* ===== Checkboxes / radios (accessible but nicer) ===== */\ninput[type=\"checkbox\"], input[type=\"radio\"] {\n accent-color: var(--color-focus-border);\n width: var(--checkbox-size);\n height: var(--checkbox-size);\n}\n\n/* ===== Buttons ===== */\n.actions,\n.form-actions {\n margin-top: 8px;\n}\n\nbutton[type=\"submit\"],\n.button-submit {\n width: 100%;\n height: var(--submit-btn-height);\n background: var(--color-submit-btn-bg);\n color: var(--color-submit-btn-text);\n border: 1px solid transparent;\n border-radius: var(--border-radius-input);\n font-size: 15px;\n font-weight: 700;\n letter-spacing: .2px;\n cursor: pointer;\n transition: transform .05s ease, filter .15s ease, box-shadow .15s ease;\n box-shadow: 0 6px 18px rgba(255, 109, 90, .25);\n}\n\nbutton[type=\"submit\"]:hover {\n filter: brightness(0.98);\n transform: translateY(-1px);\n box-shadow: 0 8px 24px rgba(255, 109, 90, .28);\n}\n\nbutton[type=\"submit\"]:active {\n transform: translateY(0);\n box-shadow: 0 6px 18px rgba(255, 109, 90, .22);\n}\n\nbutton[disabled] {\n opacity: .6;\n cursor: not-allowed;\n}\n\n/* Secondary link under button (e.g., \u201cClear form\u201d) */\n.clear-link {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin-top: 10px;\n font-size: var(--font-size-link);\n color: var(--color-link);\n text-decoration: none;\n}\n.clear-link:hover { text-decoration: underline; }\n\n/* ===== Footer / \u201cAutomated with n8n\u201d ===== */\n.form-footer {\n margin: 10px auto 24px;\n max-width: var(--container-width);\n text-align: center;\n color: var(--color-link);\n font-size: var(--font-size-link);\n}\n\n/* ===== Responsive ===== */\n@media (max-width: 520px) {\n .container, .form-wrapper, .n8n-form {\n margin: 20px auto;\n padding: 0 10px;\n }\n .form-card { padding: 20px; }\n .form-card h1, h1.form-title { font-size: 18px; }\n .form-subtitle { font-size: 13px; }\n}\n\n/* ===== Optional: Dark mode (auto) ===== */\n@media (prefers-color-scheme: dark) {\n body {\n background:\n radial-gradient(1200px 800px at 10% -10%, #272a33 0%, transparent 60%),\n radial-gradient(1000px 700px at 110% 0%, #2c2433 0%, transparent 55%),\n #16181d;\n color: #d8dbe3;\n }\n\n .test-notice {\n background: #2a2115;\n border-color: #3a2b18;\n color: #ffce74;\n }\n\n .form-card {\n background: #1c1f27;\n border-color: #2a2f3a;\n box-shadow: 0 8px 24px rgba(0,0,0,.35);\n }\n\n .form-card h1, h1.form-title { color: #e9ebf3; }\n .form-subtitle { color: #a6adbb; }\n\n input, textarea, select {\n background: #11151c;\n border-color: #2a2f3a;\n color: #d8dbe3;\n }\n input:hover, textarea:hover, select:hover { border-color: #3a4250; }\n input:focus, textarea:focus, select:focus {\n border-color: #b7a8ff;\n box-shadow: 0 0 0 4px rgba(183,168,255,.18);\n }\n\n .help-text { color: #9aa3b2; }\n\n button[type=\"submit\"],\n .button-submit {\n box-shadow: 0 10px 26px rgba(255,109,90,.34);\n }\n\n .form-footer { color: #9aa3b2; }\n}"
},
"operation": "completion",
"completionTitle": "Request sent for your manager\u2019s approval",
"completionMessage": "Soon you will receive a response on whether your request has been approved."
},
"typeVersion": 2.3
},
{
"id": "cbb41697-dee5-40d1-9ae9-1618e0da91c2",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
2544,
400
],
"parameters": {
"jsonSchemaExample": "{\n\t\"email_text\": \"California\",\n\t\"sanitized_request\": \"request\"\n}"
},
"typeVersion": 1.3
},
{
"id": "eec5a816-c41b-4d53-80b5-0d73ffe7c92f",
"name": "Notify approved request",
"type": "n8n-nodes-base.gmail",
"position": [
4480,
80
],
"parameters": {
"sendTo": "={{ $('Data and message').item.json.employee.email }}",
"message": "=Dear {{ $('Data and message').item.json.employee.name }},\n\nYour request for the following item(s) has been approved and forwarded to the Procurement Department:\n\n{{ $('Data and message').item.json.output.sanitized_request }}\n\nThe purchasing team will proceed with the necessary steps and contact you if additional details are required.\n\nBest regards, \nEquipment request AI\n",
"options": {},
"subject": "Equipment Request Approved",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "1b39aac6-8136-4f70-b59b-df0dd81bbcd9",
"name": "Notify denied request",
"type": "n8n-nodes-base.gmail",
"position": [
4480,
416
],
"parameters": {
"sendTo": "={{ $('Data and message').item.json.employee.email }}",
"message": "=Dear {{ $('Data and message').item.json.employee.name }},\n\nYour request for the following item(s) has been approved and forwarded to the Procurement Department:\n\n{{ $('Data and message').item.json.output.sanitized_request }}\n\nThe purchasing team will proceed with the necessary steps and contact you if additional details are required.\n\nBest regards, \nEquipment request AI\n",
"options": {},
"subject": "Equipment Request Approved",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "ff97d6fb-ede9-4cfb-bf37-080fba03ead8",
"name": "OpenAI Chat Model1",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
2304,
880
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-nano",
"cachedResultName": "gpt-4.1-nano"
},
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "d8260916-6491-4b73-94cc-7bf1e8e0e42c",
"name": "Structured Output Parser1",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
2528,
880
],
"parameters": {
"jsonSchemaExample": "{\n\t\"email_text\": \"California\",\n\t\"sanitized_request\": \"request\"\n}"
},
"typeVersion": 1.3
},
{
"id": "f5d570f0-16f8-4610-8484-5d8338a95cae",
"name": "Create request message",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
2336,
736
],
"parameters": {
"text": "=Employee request:\n\n{{ $json['What would you like to request?'] }}\n",
"batching": {},
"messages": {
"messageValues": [
{
"message": "=You are a writing assistant. \nYou will receive an equipment/item request. \nYour tasks are: \n1. Sanitize the request (fix grammar, remove unnecessary words, strange characters, or informal language). \n2. Rewrite it into a clear, professional, and polite approval request message to a manager. \n3. Output the result as a valid JSON object containing both the improved request and the full e-mail text. \n\nKeep the tone formal, concise, and business-appropriate. \nDo not invent information; only improve grammar, clarity, and tone. \n\nFormat strictly as JSON, like this:\n\n{\n \"sanitized_request\": \"[sanitized and improved version of the request]\",\n \"email_text\": \"Dear {{ $json.manager.name }},\\n\\nPlease approve the procurement of [sanitized_request] for employee {{ $json.employee.name }} (Enrollment: {{ $json.employee.enrollment_number }}).\\n\\nThank you for your attention to this request.\\n\\nBest regards,\\nEquipment request AI\"\n}\n\nEmployee data:\nEnrollment: {{ $json.employee.enrollment_number }}\nName: {{ $json.employee.name }}\nEmail: {{ $json.employee.email }}\n\nManager data:\nName: {{ $json.manager.name }}\nEmail: {{ $json.manager.email }}\n"
}
]
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.7
},
{
"id": "7c273ebb-019b-4eed-ba76-254437904b2f",
"name": "OpenAI Chat Model2",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
4832,
416
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-nano",
"cachedResultName": "gpt-4.1-nano"
},
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "d7f6d4ff-5c14-46bd-8f88-57fbe9999058",
"name": "Structured Output Parser2",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
5088,
416
],
"parameters": {
"jsonSchemaExample": "{\n\t\"email_text\": \"California\",\n\t\"sanitized_request\": \"request\"\n}"
},
"typeVersion": 1.3
},
{
"id": "bd8d035b-b0da-46c6-9c8b-1712095f6df0",
"name": "Create request approved message",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
4880,
256
],
"parameters": {
"text": "=Employee request:\n\n{{ $('Data and message').item.json['What would you like to request?'] }}\n",
"batching": {},
"messages": {
"messageValues": [
{
"message": "=You are a writing assistant. \nYou will receive an equipment/item request that has already been approved by the employee\u2019s manager. \nYour tasks are: \n1. Sanitize the request (fix grammar, remove unnecessary words, strange characters, or informal language). \n2. Rewrite it into a clear, professional, and polite procurement request e-mail addressed to the Purchasing Department. \n3. Explicitly state in the message that the request was approved by the employee\u2019s manager. \n4. Output the result as a valid JSON object containing both the improved request and the full e-mail text. \n\nKeep the tone formal, concise, and business-appropriate. \nDo not invent information; only improve grammar, clarity, and tone. \n\nFormat strictly as JSON, like this:\n\n{\n \"sanitized_request\": \"[sanitized and improved version of the request]\",\n \"email_text\": \"Dear Purchasing Department,\\n\\nPlease proceed with the procurement of [sanitized_request] for employee {{ $('Data and message').item.json.employee.name }} (Enrollment: {{ $('Data and message').item.json.employee.enrollment_number }}). This request has been reviewed and approved by the manager {{ $('Data and message').item.json.manager.name }}.\\n\\nThank you for your attention to this request.\\n\\nBest regards,\\nEquipment request AI\"\n}\n\nEmployee data: \nEnrollment: {{ $('Data and message').item.json.employee.enrollment_number }} \nName: {{ $('Data and message').item.json.employee.name }} \nEmail: {{ $('Data and message').item.json.employee.email }} \n\nManager data: \nName: {{ $('Data and message').item.json.manager.name }} \nEmail: {{ $('Data and message').item.json.manager.email }} \n"
}
]
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.7
},
{
"id": "c4896c62-35a5-4c34-b97f-0c28a9aa5225",
"name": "Send request approved to the purchasing department",
"type": "n8n-nodes-base.noOp",
"position": [
5440,
256
],
"parameters": {},
"typeVersion": 1
},
{
"id": "7374c685-d578-4b89-a730-21f12c1e3168",
"name": "Send request to the purchasing department",
"type": "n8n-nodes-base.noOp",
"position": [
2880,
736
],
"parameters": {},
"typeVersion": 1
},
{
"id": "9f0e9bdd-d1ee-480c-8808-624dfbb661bf",
"name": "End manager request",
"type": "n8n-nodes-base.form",
"position": [
3360,
736
],
"parameters": {
"options": {
"customCss": "/* ===== Base layout ===== */\nhtml, body {\n height: 100%;\n font-family: var(--font-family);\n font-weight: var(--font-weight-normal);\n font-size: var(--font-size-body);\n background:\n radial-gradient(1200px 800px at 10% -10%, #f0f2ff 0%, transparent 60%),\n radial-gradient(1000px 700px at 110% 0%, #fef3f0 0%, transparent 55%),\n var(--color-background);\n color: var(--color-html-text);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n.container, .form-wrapper, .n8n-form {\n max-width: var(--container-width);\n margin: 32px auto;\n padding: 0 12px;\n}\n\n/* ===== Test banner (top) ===== */\n.test-notice {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 auto 12px;\n max-width: var(--container-width);\n background: var(--color-test-notice-bg);\n border: 1px solid var(--color-test-notice-border);\n color: var(--color-test-notice-text);\n padding: var(--padding-test-notice-vertical) var(--padding-test-notice-horizontal);\n border-radius: var(--border-radius-input);\n font-size: var(--font-size-test-notice);\n}\n\n/* ===== Card ===== */\n.form-card {\n background: var(--color-card-bg);\n border: 1px solid var(--color-card-border);\n border-radius: var(--border-radius-card);\n box-shadow: var(--box-shadow-card);\n padding: var(--padding-card);\n margin-bottom: var(--margin-bottom-card);\n}\n\n/* ===== Headings & subtitle ===== */\n.form-card h1,\nh1.form-title {\n font-size: var(--font-size-header);\n color: var(--color-header);\n margin: 4px 0 6px;\n font-weight: var(--font-weight-bold);\n letter-spacing: -0.2px;\n}\n\n.form-subtitle {\n font-size: var(--font-size-paragraph);\n color: var(--color-header-subtext);\n margin: 0 0 16px;\n line-height: 1.5;\n}\n\n/* ===== Form fields ===== */\n.form-field {\n display: grid;\n gap: 6px;\n margin-bottom: 14px;\n}\n\n.form-field label {\n font-size: var(--font-size-label);\n color: var(--color-label);\n font-weight: 600;\n}\n\n.form-field label .required {\n color: var(--color-required);\n margin-left: 2px;\n}\n\n/* Inputs / selects / textareas */\ninput[type=\"text\"],\ninput[type=\"email\"],\ninput[type=\"number\"],\ninput[type=\"search\"],\ntextarea,\nselect {\n width: 100%;\n appearance: none;\n border: 1px solid var(--color-input-border);\n border-radius: var(--border-radius-input);\n padding: 12px 14px;\n font-size: var(--font-size-input);\n color: var(--color-input-text);\n background: #fff;\n transition: border-color .15s ease, box-shadow .15s ease, transform .05s ease;\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: var(--opacity-placeholder);\n}\n\n/* Hover / focus */\ninput:hover,\ntextarea:hover,\nselect:hover {\n border-color: #cfd5e2;\n}\n\ninput:focus,\ntextarea:focus,\nselect:focus {\n outline: none;\n border-color: var(--color-focus-border);\n box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* Invalid */\n.is-invalid,\ninput[aria-invalid=\"true\"],\ntextarea[aria-invalid=\"true\"],\nselect[aria-invalid=\"true\"] {\n border-color: var(--color-error);\n box-shadow: 0 0 0 4px rgba(234, 31, 48, .12);\n}\n\n/* Help / error text */\n.help-text {\n font-size: 12px;\n color: var(--color-link);\n}\n\n.error-text {\n font-size: var(--font-size-error);\n color: var(--color-error);\n margin-top: 4px;\n}\n\n/* ===== File input (custom) ===== */\n.input-file {\n position: relative;\n}\n\n.input-file input[type=\"file\"] {\n position: absolute;\n inset: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n.input-file .file-ui {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n border: 1px dashed var(--color-input-border);\n border-radius: var(--border-radius-input);\n padding: 12px 14px;\n color: var(--color-input-text);\n background: #fff;\n transition: border-color .15s ease, box-shadow .15s ease;\n}\n\n.input-file .file-ui:hover {\n border-color: #cfd5e2;\n}\n\n.input-file .file-ui:has(+ input[type=\"file\"]:focus) {\n border-color: var(--color-focus-border);\n box-shadow: 0 0 0 4px rgba(90, 76, 194, .12);\n}\n\n/* ===== Checkboxes / radios (accessible but nicer) ===== */\ninput[type=\"checkbox\"], input[type=\"radio\"] {\n accent-color: var(--color-focus-border);\n width: var(--checkbox-size);\n height: var(--checkbox-size);\n}\n\n/* ===== Buttons ===== */\n.actions,\n.form-actions {\n margin-top: 8px;\n}\n\nbutton[type=\"submit\"],\n.button-submit {\n width: 100%;\n height: var(--submit-btn-height);\n background: var(--color-submit-btn-bg);\n color: var(--color-submit-btn-text);\n border: 1px solid transparent;\n border-radius: var(--border-radius-input);\n font-size: 15px;\n font-weight: 700;\n letter-spacing: .2px;\n cursor: pointer;\n transition: transform .05s ease, filter .15s ease, box-shadow .15s ease;\n box-shadow: 0 6px 18px rgba(255, 109, 90, .25);\n}\n\nbutton[type=\"submit\"]:hover {\n filter: brightness(0.98);\n transform: translateY(-1px);\n box-shadow: 0 8px 24px rgba(255, 109, 90, .28);\n}\n\nbutton[type=\"submit\"]:active {\n transform: translateY(0);\n box-shadow: 0 6px 18px rgba(255, 109, 90, .22);\n}\n\nbutton[disabled] {\n opacity: .6;\n cursor: not-allowed;\n}\n\n/* Secondary link under button (e.g., \u201cClear form\u201d) */\n.clear-link {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin-top: 10px;\n font-size: var(--font-size-link);\n color: var(--color-link);\n text-decoration: none;\n}\n.clear-link:hover { text-decoration: underline; }\n\n/* ===== Footer / \u201cAutomated with n8n\u201d ===== */\n.form-footer {\n margin: 10px auto 24px;\n max-width: var(--container-width);\n text-align: center;\n color: var(--color-link);\n font-size: var(--font-size-link);\n}\n\n/* ===== Responsive ===== */\n@media (max-width: 520px) {\n .container, .form-wrapper, .n8n-form {\n margin: 20px auto;\n padding: 0 10px;\n }\n .form-card { padding: 20px; }\n .form-card h1, h1.form-title { font-size: 18px; }\n .form-subtitle { font-size: 13px; }\n}\n\n/* ===== Optional: Dark mode (auto) ===== */\n@media (prefers-color-scheme: dark) {\n body {\n background:\n radial-gradient(1200px 800px at 10% -10%, #272a33 0%, transparent 60%),\n radial-gradient(1000px 700px at 110% 0%, #2c2433 0%, transparent 55%),\n #16181d;\n color: #d8dbe3;\n }\n\n .test-notice {\n background: #2a2115;\n border-color: #3a2b18;\n color: #ffce74;\n }\n\n .form-card {\n background: #1c1f27;\n border-color: #2a2f3a;\n box-shadow: 0 8px 24px rgba(0,0,0,.35);\n }\n\n .form-card h1, h1.form-title { color: #e9ebf3; }\n .form-subtitle { color: #a6adbb; }\n\n input, textarea, select {\n background: #11151c;\n border-color: #2a2f3a;\n color: #d8dbe3;\n }\n input:hover, textarea:hover, select:hover { border-color: #3a4250; }\n input:focus, textarea:focus, select:focus {\n border-color: #b7a8ff;\n box-shadow: 0 0 0 4px rgba(183,168,255,.18);\n }\n\n .help-text { color: #9aa3b2; }\n\n button[type=\"submit\"],\n .button-submit {\n box-shadow: 0 10px 26px rgba(255,109,90,.34);\n }\n\n .form-footer { color: #9aa3b2; }\n}\n"
},
"operation": "completion",
"completionTitle": "Request sent to the purchasing department",
"completionMessage": "Your request has already been forwarded to the purchasing department."
},
"typeVersion": 2.3
},
{
"id": "2411f09c-2392-48d2-83a4-3ce0d087b8ea",
"name": "Send request to approve",
"type": "n8n-nodes-base.gmail",
"position": [
3696,
272
],
"parameters": {
"sendTo": "={{ $('Data and message').item.json.manager.email }}",
"message": "={{ $('Data and message').item.json.output.email_text }}",
"options": {},
"subject": "=Equipment Request Approval \u2013 {{ $('Data and message').item.json.employee.name }} (Enrollment:{{ $('Data and message').item.json.employee.enrollment_number }})",
"operation": "sendAndWait",
"approvalOptions": {
"values": {
"approvalType": "double"
}
}
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "4f63e8a7-04d8-491d-acea-1a46108182dc",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
-1056
],
"parameters": {
"width": 1072,
"height": 544,
"content": "## Equipment Request \u2014 Approval & Procurement Flow\n\nThis template lets employees request equipment/items. If the employee has a manager, the request is sent for approval; if approved, it goes to Procurement. If the employee is a manager (no manager record), it goes straight to Procurement.\n\nFlow:\n1) Employee enters enrollment number (ID).\n2) System fetches employee (and manager, if any).\n3) Employee describes the request.\n4) If approval is required \u2192 email approval to manager (double opt-in).\n5) If approved \u2192 notify employee and send a polished request to Procurement.\n6) If employee is a manager \u2192 skip approval and go straight to Procurement.\n7) End pages inform the submitter.\n\nSecurity:\n- No hardcoded credentials. Use n8n credentials.\n- Avoid exposing employee PII in logs.\n- Sanitize free text before sending emails.\n\nSuccess criteria:\n- Employee matched correctly.\n- Approval branch triggers when manager exists.\n- Clear emails generated (sanitized) for manager and Procurement.\n- Notifications sent and final pages rendered without errors.\n"
},
"typeVersion": 1
},
{
"id": "13f364a2-b86e-4283-8fce-fa11e3e3d5f7",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
-336
],
"parameters": {
"color": 4,
"width": 416,
"height": 400,
"content": "Collects the employee enrollment number.\n\nTip:\n- Make the label/instructions clear (e.g., where to find the number).\n- Consider adding help text and validation regex if your IDs are structured.\n"
},
"typeVersion": 1
},
{
"id": "d35815c9-44fb-465e-b2a0-19badd10711c",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-128,
-336
],
"parameters": {
"color": 5,
"width": 416,
"height": 400,
"content": "Looks up the employee by enrollment_number in `employees`.\n\nExpected columns:\n- id, name, email, enrollment_number, manager (nullable)\n\nAlways outputs data to allow the IF node to decide next steps.\n"
},
"typeVersion": 1
},
{
"id": "8a81d631-b8dd-4772-8bde-24d698b73074",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
304,
-336
],
"parameters": {
"color": 6,
"width": 304,
"height": 400,
"content": "Checks if the DB lookup returned empty.\n\nTRUE \u2192 Show \"Employee not found\".\nFALSE \u2192 Continue with the process.\n"
},
"typeVersion": 1
},
{
"id": "bb7851a9-a5cc-414f-bd1b-bae07fb2af89",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
624,
-528
],
"parameters": {
"color": 5,
"width": 304,
"height": 368,
"content": "User-facing message when we cannot match the enrollment number.\n\nCustomize copy:\n- Provide support channel or HR email for corrections.\n"
},
"typeVersion": 1
},
{
"id": "1b55d378-cdd2-44a3-a71a-7b5421bd4cb6",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
624,
-144
],
"parameters": {
"color": 5,
"width": 304,
"height": 400,
"content": "Normalizes the employee object for later nodes:\n- employee.enrollment_number, employee.name, employee.email, employee.manager\n- employee.requires_approval = !!manager\n\nKeeps data tidy for merging and prompting the LLM.\n"
},
"typeVersion": 1
},
{
"id": "f28a39e9-3817-410c-bf68-949bebc16b3f",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
944,
-144
],
"parameters": {
"color": 6,
"width": 304,
"height": 368,
"content": "Branching:\n- TRUE (has manager) \u2192 fetch manager and request approval path.\n- FALSE (no manager) \u2192 direct to Procurement path.\n"
},
"typeVersion": 1
},
{
"id": "1d511e50-b193-432f-8115-d8171790948f",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
1264,
-144
],
"parameters": {
"color": 5,
"width": 304,
"height": 368,
"content": "Fetches the manager\u2019s record from `employees` by `id = employee.manager`.\n\nExpected columns:\n- id, name, email\n"
},
"typeVersion": 1
},
{
"id": "b00441ed-2399-485f-a129-1fb0e60cba0f",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
1584,
-144
],
"parameters": {
"color": 5,
"width": 304,
"height": 368,
"content": "Wraps the manager row into a single `manager` object for easy access.\n"
},
"typeVersion": 1
},
{
"id": "66ac3153-bab7-4ad1-a878-9105a390b060",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
1904,
-144
],
"parameters": {
"color": 6,
"width": 304,
"height": 368,
"content": "Merges employee and manager streams after DB lookup.\nEnsures downstream nodes have both objects available.\n"
},
"typeVersion": 1
},
{
"id": "50a8c7f3-a77e-4daa-942e-a98af2ff699a",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
-560,
304
],
"parameters": {
"color": 4,
"width": 416,
"height": 400,
"content": "Asks the employee what they want to request (free text).\n\nStyling:\n- Same CSS system used across forms for consistent UX.\n"
},
"typeVersion": 1
},
{
"id": "6f027668-fc90-4824-9858-09b48e3ceade",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"position": [
1264,
320
],
"parameters": {
"color": 6,
"width": 304,
"height": 368,
"content": "Combines the employee context and the free-text request into one payload.\nDownstream nodes can read both without juggling inputs.\n"
},
"typeVersion": 1
},
{
"id": "7ff2a7ab-94b6-48ca-82d5-0c09ed386233",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"position": [
1584,
320
],
"parameters": {
"color": 6,
"width": 304,
"height": 368,
"content": "Simple gate:\n- TRUE \u2192 Build an approval email to manager.\n- FALSE \u2192 Build a Procurement email (employee is a manager).\n"
},
"typeVersion": 1
},
{
"id": "d15fafb7-c9a3-4cc2-aa85-be88371d809f",
"name": "Sticky Note13",
"type": "n8n-nodes-base.stickyNote",
"position": [
2224,
48
],
"parameters": {
"color": 5,
"width": 464,
"height": 480,
"content": "Prompts the LLM to:\n1) Sanitize the request text.\n2) Produce a clear approval email for the manager.\n3) Return strict JSON: { sanitized_request, email_text }.\n\nKeep tone formal and concise; no invented details.\n"
},
"typeVersion": 1
},
{
"id": "15864539-f1ae-45ed-ae6f-a7fa57bc6507",
"name": "Sticky Note14",
"type": "n8n-nodes-base.stickyNote",
"position": [
2704,
48
],
"parameters": {
"color": 5,
"width": 336,
"height": 480,
"content": "Combines the sanitized output with employee/manager context for email nodes.\n"
},
"typeVersion": 1
},
{
"id": "23d85284-cddc-4070-9d55-ff137832fcad",
"name": "Sticky Note15",
"type": "n8n-nodes-base.stickyNote",
"position": [
3056,
48
],
"parameters": {
"color": 3,
"width": 464,
"height": 448,
"content": "Final screen for the requester after submission in \u201capproval required\u201d path.\n\nMessaging:\n- Confirms the request was sent to the manager for approval.\n- Sets expectations for response time.\n"
},
"typeVersion": 1
},
{
"id": "2a5c9acd-e1b9-4682-afac-6b326a417a11",
"name": "Sticky Note16",
"type": "n8n-nodes-base.stickyNote",
"position": [
3536,
48
],
"parameters": {
"color": 4,
"width": 416,
"height": 448,
"content": "Emails the manager with `email_text` and waits for approval/denial (double approval).\n\nOutputs:\n- data.approved = true/false\n\nEnsure your Gmail credential has send permission
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.
gmailOAuth2mySqlopenAiApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow streamlines how employees request equipment/items and how those requests reach the Procurement team. It validates the employee by enrollment number, detects whether a manager exists, and then either requests approval (if the requester has a manager) or routes the…
Source: https://n8n.io/workflows/8728/ — 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.
This template attempts to replicate OpenAI's DeepResearch feature which, at time of writing, is only available to their pro subscribers.
My workflow 53. Uses formTrigger, httpRequest, lmChatOpenAi, form. Event-driven trigger; 74 nodes.
This workflow serves as a comprehensive "Workflow Nodes SEO & Documentation Generator". It uses AI to analyze, rename, and document n8n workflows, offering a streamlined way to optimize workflow reada
This workflow is perfect for: Agile development teams and project managers who need to quickly set up Jira projects Product managers who want to convert feature ideas into structured user stories and
The workflow runs every hour with a randomized delay of 5–20 minutes to help distribute load. It records the exact date and time a lead is emailed so you can track outreach. Follow-ups are automatical