This workflow corresponds to n8n.io template #17317 — 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": "UzNU7L6PsMjPobQL",
"meta": {
"builderVariant": "mcp",
"aiBuilderAssisted": true
},
"name": "Review your resume against a job description with Gemini and email the report",
"tags": [],
"nodes": [
{
"id": "50b3d9f1-5ea6-4b07-a794-c8c89cfc6b93",
"name": "Resume Review Form",
"type": "n8n-nodes-base.formTrigger",
"position": [
48,
48
],
"parameters": {
"options": {
"buttonLabel": "Check my resume",
"respondWithOptions": {
"values": {
"formSubmittedText": "Thanks! Your report is being generated and will arrive by email shortly."
}
}
},
"formTitle": "Resume vs job description check",
"formFields": {
"values": [
{
"fieldType": "email",
"fieldLabel": "Your email",
"requiredField": true
},
{
"fieldLabel": "Target job title",
"requiredField": true
},
{
"fieldType": "textarea",
"fieldLabel": "Job description",
"requiredField": true
},
{
"fieldType": "file",
"fieldLabel": "Resume",
"multipleFiles": false,
"requiredField": true,
"acceptFileTypes": ".pdf"
}
]
},
"formDescription": "Upload your resume and paste the job description. You will get a match score and concrete improvements by email."
},
"typeVersion": 2.6
},
{
"id": "b8aea2e4-0006-4993-80bb-d425c0087a99",
"name": "Extract Resume Text",
"type": "n8n-nodes-base.extractFromFile",
"position": [
272,
48
],
"parameters": {
"options": {},
"operation": "pdf",
"binaryPropertyName": "Resume"
},
"typeVersion": 1.1
},
{
"id": "94057080-e7cc-413a-a6fe-c09128b4955e",
"name": "Compare Resume to Job with Gemini",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"onError": "continueErrorOutput",
"position": [
608,
48
],
"parameters": {
"text": "=You are an experienced recruiter and resume coach. Compare the resume below against the job description and score the match from 0 to 100. Set verdict to \"strong\" when the candidate is clearly a fit, \"partial\" when they could be a fit with edits, and \"weak\" when the gap is large. List the important keywords from the job description that are missing from the resume, the genuine strengths, concrete improvements the candidate should make, and a rewritten professional summary tailored to this role.\n\nTarget role: {{ $('Resume Review Form').item.json['Target job title'] }}\n\nJob description:\n{{ $('Resume Review Form').item.json['Job description'] }}\n\nResume:\n{{ $json.text }}",
"batching": {},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.9
},
{
"id": "685353ab-e281-45e1-b260-a6e784e36aab",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
576,
224
],
"parameters": {
"options": {
"temperature": 0.2
},
"modelName": "models/gemini-3.1-flash-lite"
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.1
},
{
"id": "690c5895-7712-4cfa-9008-0baad16ccc96",
"name": "Match Result Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
816,
224
],
"parameters": {
"jsonSchemaExample": "{\"match_score\":78,\"verdict\":\"partial\",\"missing_keywords\":[\"Kubernetes\"],\"strengths\":[\"Strong Python background\"],\"improvements\":[\"Add measurable impact to each role\"],\"rewritten_summary\":\"Backend engineer with 5 years...\"}"
},
"typeVersion": 1.3
},
{
"id": "e6970c86-18ed-4a55-8031-e713b5610b6a",
"name": "Parse Match Result",
"type": "n8n-nodes-base.code",
"position": [
1232,
32
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const src = $json.output || $json;\nconst f = $('Resume Review Form').item.json;\nconst join = function (x) { return Array.isArray(x) ? x.join('; ') : (x || ''); };\nconst bullets = function (x) { return Array.isArray(x) ? x.map(function (i) { return '- ' + i; }).join('\\n') : (x || ''); };\nreturn {\n email: f['Your email'] || '',\n jobTitle: f['Target job title'] || '',\n matchScore: Number(src.match_score) || 0,\n verdict: String(src.verdict || 'partial').toLowerCase(),\n missingKeywords: join(src.missing_keywords),\n strengths: join(src.strengths),\n improvements: join(src.improvements),\n improvementList: bullets(src.improvements),\n missingList: bullets(src.missing_keywords),\n rewrittenSummary: src.rewritten_summary || ''\n};"
},
"typeVersion": 2
},
{
"id": "5f0a6c3a-ab9b-4ec0-ace3-c41cac41fa68",
"name": "Log Review",
"type": "n8n-nodes-base.googleSheets",
"position": [
1456,
32
],
"parameters": {
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Resume Reviews"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Select your spreadsheet"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "767db5fe-3477-494c-aeab-a5df90c587c5",
"name": "Route by Verdict",
"type": "n8n-nodes-base.switch",
"position": [
1824,
0
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $(\"Parse Match Result\").item.json.verdict }}",
"rightValue": "strong"
}
]
}
},
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $(\"Parse Match Result\").item.json.verdict }}",
"rightValue": "partial"
}
]
}
},
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $(\"Parse Match Result\").item.json.verdict }}",
"rightValue": "weak"
}
]
}
}
]
},
"options": {
"fallbackOutput": "extra",
"renameFallbackOutput": "Other"
}
},
"typeVersion": 3.2
},
{
"id": "8223f1cc-5bb8-438f-bfef-bc2a05d1a19f",
"name": "Email Strong Match Report",
"type": "n8n-nodes-base.gmail",
"position": [
2064,
-16
],
"parameters": {
"sendTo": "={{ $(\"Parse Match Result\").item.json.email }}",
"message": "={{ \"You are a strong fit for this role - apply now.\\n\\nStrengths:\\n\" + $(\"Parse Match Result\").item.json.strengths + \"\\n\\nPolish these before you send it:\\n\" + $(\"Parse Match Result\").item.json.improvementList + \"\\n\\nSuggested professional summary:\\n\" + $(\"Parse Match Result\").item.json.rewrittenSummary }}",
"options": {},
"subject": "={{ \"Strong match (\" + $(\"Parse Match Result\").item.json.matchScore + \"/100) - \" + $(\"Parse Match Result\").item.json.jobTitle }}",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "d70a93ef-6acd-43b1-975d-d18e6a665312",
"name": "Email Improvement Report",
"type": "n8n-nodes-base.gmail",
"position": [
2064,
160
],
"parameters": {
"sendTo": "={{ $(\"Parse Match Result\").item.json.email }}",
"message": "={{ \"You could be a fit with some edits.\\n\\nKeywords the job asks for that your resume is missing:\\n\" + $(\"Parse Match Result\").item.json.missingList + \"\\n\\nDo these before applying:\\n\" + $(\"Parse Match Result\").item.json.improvementList + \"\\n\\nSuggested professional summary:\\n\" + $(\"Parse Match Result\").item.json.rewrittenSummary }}",
"options": {},
"subject": "={{ \"Close - \" + $(\"Parse Match Result\").item.json.matchScore + \"/100 for \" + $(\"Parse Match Result\").item.json.jobTitle }}",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "a126b089-b3b1-40da-8b45-3b49b261b18d",
"name": "Email Rework Plan",
"type": "n8n-nodes-base.gmail",
"position": [
2064,
336
],
"parameters": {
"sendTo": "={{ $(\"Parse Match Result\").item.json.email }}",
"message": "={{ \"This role is a stretch right now, and that is useful to know early.\\n\\nWhat the job wants that you do not show yet:\\n\" + $(\"Parse Match Result\").item.json.missingList + \"\\n\\nHow to close the gap:\\n\" + $(\"Parse Match Result\").item.json.improvementList + \"\\n\\nWhat already works in your favour:\\n\" + $(\"Parse Match Result\").item.json.strengths }}",
"options": {},
"subject": "={{ \"Big gap - \" + $(\"Parse Match Result\").item.json.matchScore + \"/100 for \" + $(\"Parse Match Result\").item.json.jobTitle }}",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "55e63a1d-b828-4f3b-9c17-25a2cd376e94",
"name": "Email Generic Report",
"type": "n8n-nodes-base.gmail",
"position": [
2064,
512
],
"parameters": {
"sendTo": "={{ $(\"Parse Match Result\").item.json.email }}",
"message": "={{ \"Match score: \" + $(\"Parse Match Result\").item.json.matchScore + \"/100\\n\\nStrengths:\\n\" + $(\"Parse Match Result\").item.json.strengths + \"\\n\\nImprovements:\\n\" + $(\"Parse Match Result\").item.json.improvementList }}",
"options": {},
"subject": "={{ \"Your resume review - \" + $(\"Parse Match Result\").item.json.jobTitle }}",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "57d5d280-9960-4f3d-bf32-3ad0ca1a6e49",
"name": "Email Analysis Failure",
"type": "n8n-nodes-base.gmail",
"position": [
992,
176
],
"parameters": {
"sendTo": "={{ $(\"Resume Review Form\").item.json[\"Your email\"] }}",
"message": "Sorry - we could not read your resume file. Please re-submit a text-based PDF (not a scanned image).",
"options": {},
"subject": "We could not analyse your resume",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "2e486d9d-897b-40f8-b559-4395a1321ddc",
"name": "Overview Sticky",
"type": "n8n-nodes-base.stickyNote",
"position": [
-464,
-288
],
"parameters": {
"width": 440,
"height": 724,
"content": "## Review your resume against a job description with Gemini and email the report\n\n### How it works\nMost applications fail because the resume never mirrors the job description. Upload a resume and paste the posting into the built-in n8n form, and a Basic LLM Chain with Google Gemini reads both and returns a 0-100 match score, the keywords the posting asks for that your resume is missing, your genuine strengths, concrete improvements, and a rewritten professional summary tailored to that role. Every review is logged to Google Sheets so you can track applications, and a Switch sends a different email depending on the verdict: strong gets an apply-now nudge, partial gets an edit list, weak gets an honest gap-closing plan.\n\n### Setup\n1. Connect Google Gemini (PaLM) API, Google Sheets and Gmail.\n2. Point the Sheets node at your tracker spreadsheet.\n3. Open the form URL and submit a text-based PDF resume.\n\n### Customization tips\nChange the score bands, or add a second AI call that rewrites the whole resume."
},
"typeVersion": 1
},
{
"id": "033264dd-9b24-41a6-af52-660211b26ed2",
"name": "S1",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
-288
],
"parameters": {
"color": 7,
"width": 500,
"height": 1028,
"content": "## 1. Submit resume & posting\nThe built-in n8n form collects the email, target role, job description and the resume PDF, then the text is extracted."
},
"typeVersion": 1
},
{
"id": "6448fade-776e-4e16-9a86-5476d6519dd0",
"name": "S2",
"type": "n8n-nodes-base.stickyNote",
"position": [
528,
-288
],
"parameters": {
"color": 7,
"width": 596,
"height": 1028,
"content": "## 2. AI comparison (Basic LLM Chain)\nGemini scores the match and returns missing keywords, strengths, improvements and a tailored summary. Unreadable PDFs trigger an email back."
},
"typeVersion": 1
},
{
"id": "7be5b651-6dd7-4595-88c1-bb3fbc859ec3",
"name": "S3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1152,
-288
],
"parameters": {
"color": 7,
"width": 548,
"height": 1028,
"content": "## 3. Parse & log\nFlatten the result and append it to your application tracker in Google Sheets."
},
"typeVersion": 1
},
{
"id": "31343559-416e-4020-83d0-937de9b1e2ca",
"name": "S4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1728,
-288
],
"parameters": {
"color": 7,
"width": 584,
"height": 1044,
"content": "## 4. Route by verdict\nStrong gets an apply-now email, partial gets an edit list, weak gets a gap-closing plan, anything unclear gets the generic report."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": true,
"executionOrder": "v1"
},
"versionId": "babdd4aa-3756-44f9-9c8d-23fb0030f765",
"nodeGroups": [],
"connections": {
"Log Review": {
"main": [
[
{
"node": "Route by Verdict",
"type": "main",
"index": 0
}
]
]
},
"Route by Verdict": {
"main": [
[
{
"node": "Email Strong Match Report",
"type": "main",
"index": 0
}
],
[
{
"node": "Email Improvement Report",
"type": "main",
"index": 0
}
],
[
{
"node": "Email Rework Plan",
"type": "main",
"index": 0
}
],
[
{
"node": "Email Generic Report",
"type": "main",
"index": 0
}
]
]
},
"Parse Match Result": {
"main": [
[
{
"node": "Log Review",
"type": "main",
"index": 0
}
]
]
},
"Resume Review Form": {
"main": [
[
{
"node": "Extract Resume Text",
"type": "main",
"index": 0
}
]
]
},
"Extract Resume Text": {
"main": [
[
{
"node": "Compare Resume to Job with Gemini",
"type": "main",
"index": 0
}
]
]
},
"Match Result Parser": {
"ai_outputParser": [
[
{
"node": "Compare Resume to Job with Gemini",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Compare Resume to Job with Gemini",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Compare Resume to Job with Gemini": {
"main": [
[
{
"node": "Parse Match Result",
"type": "main",
"index": 0
}
],
[
{
"node": "Email Analysis Failure",
"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.
gmailOAuth2googlePalmApigoogleSheetsOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow collects a resume PDF and job description via an n8n form, extracts resume text, uses Google Gemini to generate a match score and improvement report, logs the results to Google Sheets, and emails a tailored response through Gmail based on the verdict. Receives a…
Source: https://n8n.io/workflows/17317/ — 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.
Automate your lead intake, scoring, and outreach pipeline. This workflow collects leads from forms, enriches and scores them using Relevance AI, routes them by quality, and triggers the right follow-u
This workflow collects a PDF and study preferences via an n8n form, extracts its text, uses Google Gemini to generate flashcards, a quiz, key topics, and a study plan, then saves the cards and quiz to
This workflow collects a CSV bank statement via an n8n form, detects stable recurring charges, and uses Google Gemini to classify subscriptions and recommend keep/review/cancel. It emails the results
This workflow collects a plant photo via an n8n form, uses Google Gemini to identify the plant and assess its health, logs the result to Google Sheets, and emails a tailored care plan through Gmail ba
leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.