This workflow corresponds to n8n.io template #14138 — we link there as the canonical source.
This workflow follows the Agent → Emailsend 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": "433bbc96-565d-47da-811f-4936022a9861",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
304,
1216
],
"parameters": {
"color": 5,
"width": 1400,
"height": 280,
"content": "## \ud83e\udd16 AI Resume Screener & Automated HR Notifier\n\nAutomatically scores job applicants using Google Gemini and routes personalised emails to HR and candidates.\n\n**How it works:** Polls a Google Sheet every minute \u2192 skips already-processed rows \u2192 maps the applied role to a Job Description \u2192 asks Gemini to score the candidate 1\u201310 \u2192 writes the analysis back to the sheet \u2192 sends an HR alert and candidate email based on the score.\n\n**Setup checklist:**\n- Google Sheets OAuth2 credential \u2192 connect to Trigger and HTTP Request nodes\n- Google Gemini API key \u2192 connect to the Gemini Chat Model node\n- Replace Sheet ID `1iv-8fToAzxBm8ht5vg-UP_p_hQzK4-....` in all Google Sheet nodes\n- Update `fromEmail` / `toEmail` in the three Send Email nodes\n- Extend `JD_MAP` in the Code node with your own open roles"
},
"typeVersion": 1
},
{
"id": "e6176f2d-8b14-4512-9189-d28c6049a258",
"name": "Step 1 \u2014 Trigger & Filter",
"type": "n8n-nodes-base.stickyNote",
"position": [
304,
1504
],
"parameters": {
"color": 7,
"width": 640,
"height": 524,
"content": "### \ud83d\udce5 Step 1 \u2014 Trigger & Filter\n\n**Google Sheets Trigger** polls every minute and fires when a new row is added.\n\n**Read All Rows** re-fetches the full sheet so the filter has the latest state of every row.\n\n**Filter Unprocessed Rows** skips any row where column R (`col_18`) is already `'1'`, preventing duplicate processing."
},
"typeVersion": 1
},
{
"id": "027c71cd-96ce-4291-a11c-971d4023d1f5",
"name": "Step 2 \u2014 AI Assessment",
"type": "n8n-nodes-base.stickyNote",
"position": [
976,
1504
],
"parameters": {
"color": 7,
"width": 640,
"height": 528,
"content": "### \ud83e\udde0 Step 2 \u2014 AI Assessment\n\n**Extract Fields & Load JD** pulls the candidate's name, email, position, experience, and skills. It then looks up the matching Job Description from the built-in `JD_MAP`.\n\n**AI Resume Scorer** sends a structured prompt to Gemini requesting a raw JSON response containing: `score` (1\u201310), `grade`, `strengths`, `weaknesses`, `recommendation`, and `summary`.\n\n**Google Gemini Chat Model** is the sub-node that powers the AI agent above.\n\n**Parse AI Output** safely parses Gemini's JSON string and adds a `status` field: `'Shortlisted'` if score \u2265 7, otherwise `'Rejected'`."
},
"typeVersion": 1
},
{
"id": "158a24a4-956f-4120-a7e1-29fda0123fb6",
"name": "Step 3 \u2014 Sheet Update",
"type": "n8n-nodes-base.stickyNote",
"position": [
1648,
1520
],
"parameters": {
"color": 3,
"width": 420,
"height": 508,
"content": "### \ud83d\udcca Step 3 \u2014 Sheet Update\n\n**Build Sheet Request** constructs the Sheets API PUT URL and body, targeting columns J\u2013R on the exact row number of the applicant.\n\n**Update Sheet Row** calls the Google Sheets API directly via HTTP Request (using OAuth2) to write all 9 fields in a single operation \u2014 including setting column R to `'1'` as the processed flag."
},
"typeVersion": 1
},
{
"id": "4701a168-c9fd-40c1-a366-2cf87dbdf604",
"name": "Step 4 \u2014 Routing & Emails",
"type": "n8n-nodes-base.stickyNote",
"position": [
2096,
1392
],
"parameters": {
"color": 3,
"width": 720,
"height": 736,
"content": "### \ud83d\udce7 Step 4 \u2014 Score Routing & Emails\n\n**Check Score \u2265 7** branches the flow:\n\n**\u2705 True (shortlist path)**\n- **Alert HR Team** \u2014 notifies the HR inbox with the candidate's score and strengths.\n- **Wait 5s** \u2014 small delay before the candidate email goes out.\n- **Notify Shortlisted Candidate** \u2014 sends the applicant a congratulations and interview invitation.\n\n**\u274c False (rejection path)**\n- **Notify Rejected Candidate** \u2014 sends a polite, professional decline email."
},
"typeVersion": 1
},
{
"id": "2e51960c-5ff0-4211-979a-5f19ac271124",
"name": "Filter Unprocessed Rows",
"type": "n8n-nodes-base.code",
"position": [
800,
1760
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "\nconst row = $input.item.json;\n\n// Normalize keys\nconst r = {};\nfor (const key of Object.keys(row)) {\n r[key.trim()] = (row[key] ?? '').toString().trim();\n}\n\nconst email = r['Email Address'] || '';\nif (!email) return null; // skip rows with no email\n\n// col_18 = column R = Processed\nconst processedByCol = (row['col_18'] ?? '').toString().trim();\nconst processedByName = r['Processed'] || '';\nconst processed = processedByCol || processedByName;\n\nif (processed === '1') {\n return null; // skip\n}\n\nreturn { json: row };\n"
},
"typeVersion": 2
},
{
"id": "3eaf0b93-cb68-4513-b495-e78fb4e1685d",
"name": "Wait 5s",
"type": "n8n-nodes-base.wait",
"position": [
2368,
1760
],
"parameters": {},
"typeVersion": 1.1
},
{
"id": "4f3b8291-cc37-4ec0-893c-0792c0462c25",
"name": "Google Sheets Trigger",
"type": "n8n-nodes-base.googleSheetsTrigger",
"position": [
352,
1760
],
"parameters": {
"event": "rowAdded",
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 525556004
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "1iv-8fToAzxBm8ht5vg-UP_p_hQzK4-uqs2KWu06im4o"
}
},
"typeVersion": 1
},
{
"id": "091927be-bdd9-426a-97f6-16029726c5fd",
"name": "Read All Rows",
"type": "n8n-nodes-base.googleSheets",
"position": [
576,
1760
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 525556004
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "1iv-8fToAzxBm8ht5vg-UP_p_hQzK4-uqs2KWu06im4o"
}
},
"typeVersion": 4.5
},
{
"id": "255e4cc8-2c04-4e80-aadc-99337b04420a",
"name": "Extract Fields & Load JD",
"type": "n8n-nodes-base.code",
"position": [
1024,
1760
],
"parameters": {
"jsCode": "\nconst row = $input.first().json;\nconst r = {};\nfor (const key of Object.keys(row)) { r[key.trim()] = (row[key] ?? '').toString().trim(); }\n\nconst position = r['Select the Position You Are Applying For'] || '';\nconst name = r['Full Name'] || '';\nconst email = r['Email Address'] || '';\nconst experience = r['Years of Experience'] || '';\nconst skills = r['Relevant Skills'] || '';\nconst rowNum = parseInt(row['row_number']) || 0;\n\nconst JD_MAP = {\n 'Frontend Developer': 'ROLE: Frontend Developer. REQUIREMENTS: 2+ years React/Vue, HTML/CSS/JavaScript.',\n 'Backend Developer': 'ROLE: Backend Developer. REQUIREMENTS: 2+ years Node.js/Python, SQL/NoSQL.',\n 'UI/UX Designer': 'ROLE: UI/UX Designer. REQUIREMENTS: Figma, Portfolio.',\n 'Project Manager': 'ROLE: Project Manager. REQUIREMENTS: 3+ years PM experience.',\n 'Digital Marketing Executive': 'ROLE: Digital Marketing Executive. REQUIREMENTS: SEO/SEM, Google Ads.'\n};\nconst jd = JD_MAP[position] || 'General professional evaluation.';\n\nreturn [{ json: { ...r, row_number: rowNum, jd } }];\n"
},
"typeVersion": 2
},
{
"id": "e5b40133-c73c-4f69-b178-198c10514287",
"name": "AI Resume Scorer",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1248,
1760
],
"parameters": {
"text": "=You are an expert HR recruiter. Score this candidate based on JD: {{ $json.jd }}. Return JSON with score (1-10), grade, strengths, weaknesses, recommendation, and summary.",
"options": {
"systemMessage": "HR recruiter AI. Return raw JSON only. Single line."
},
"promptType": "define"
},
"typeVersion": 3.1
},
{
"id": "e829e12d-c659-40ed-85dc-179b0f1b55c6",
"name": "Google Gemini Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
1168,
1952
],
"parameters": {
"options": {}
},
"typeVersion": 1
},
{
"id": "4d8ab33b-160d-42bb-88fb-243b0b284168",
"name": "Parse AI Output",
"type": "n8n-nodes-base.code",
"position": [
1472,
1760
],
"parameters": {
"jsCode": "const aiRaw = $input.first().json.output || '';\nconst fd = $('Extract Fields & Load JD').first().json;\nlet p = { score: 5, grade: 'Maybe' };\ntry { p = JSON.parse(aiRaw); } catch(e) {}\nconst status = p.score >= 7 ? 'Shortlisted' : 'Rejected';\nreturn [{ json: { ...fd, ...p, status, processed_at: new Date().toISOString() } }];"
},
"typeVersion": 2
},
{
"id": "6270c4e8-eb44-4407-9b74-e760e8ddb88c",
"name": "Build Sheet Request",
"type": "n8n-nodes-base.code",
"position": [
1696,
1760
],
"parameters": {
"jsCode": "const d = $input.first().json;\nconst rowNum = d.row_number;\nconst range = `Form responses 1!J${rowNum}:R${rowNum}`;\nconst values = [String(d.score), d.grade, d.strengths, d.weaknesses, d.recommendation, d.summary, d.status, d.processed_at, '1'];\nreturn [{ json: { ...d, _requestBody: { range, values: [values] }, _url: `https://sheets.googleapis.com/v4/spreadsheets/1iv-8fToAzxBm8ht5vg-UP_p_hQzK4-uqs2KWu06im4o/values/${encodeURIComponent(range)}?valueInputOption=RAW` } }];"
},
"typeVersion": 2
},
{
"id": "aebf1e28-2ea8-4fcc-b153-9be1bcbc32d5",
"name": "Update Sheet Row",
"type": "n8n-nodes-base.httpRequest",
"position": [
1920,
1760
],
"parameters": {
"url": "={{ $json._url }}",
"method": "PUT",
"options": {},
"jsonBody": "={{ JSON.stringify($json._requestBody) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "googleSheetsOAuth2Api"
},
"typeVersion": 4.2
},
{
"id": "99f3237d-7403-47d5-9c2c-21358c9fc6eb",
"name": "Check Score \u2265 7",
"type": "n8n-nodes-base.if",
"position": [
2144,
1760
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c1",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.score }}",
"rightValue": 7
}
]
}
},
"typeVersion": 2.1
},
{
"id": "7dafc992-096f-4a77-932c-e2d24e465ada",
"name": "Alert HR Team",
"type": "n8n-nodes-base.emailSend",
"position": [
2368,
1584
],
"parameters": {
"html": "Candidate scored {{ $json.score }}. Strengths: {{ $json.strengths }}",
"options": {},
"subject": "=Shortlist Alert: {{ $json.name }} ({{ $json.score }}/10)",
"toEmail": "user@example.com",
"fromEmail": "user@example.com"
},
"typeVersion": 2.1
},
{
"id": "b66f1229-c0ee-4a5f-ad5a-aa99138e012d",
"name": "Notify Shortlisted Candidate",
"type": "n8n-nodes-base.emailSend",
"position": [
2592,
1760
],
"parameters": {
"html": "Congrats! You were shortlisted with a score of {{ $json.score }}.",
"options": {},
"subject": "=Interview Invitation - {{ $json.name }}",
"toEmail": "={{ $json.email }}",
"fromEmail": "user@example.com"
},
"typeVersion": 2.1
},
{
"id": "eded8fe9-3fc2-4e85-98b7-7d4688c31da5",
"name": "Notify Rejected Candidate",
"type": "n8n-nodes-base.emailSend",
"position": [
2368,
1952
],
"parameters": {
"html": "Thank you for applying. We won't be moving forward at this time.",
"options": {},
"subject": "Application Update",
"toEmail": "={{ $json.email }}",
"fromEmail": "user@example.com"
},
"typeVersion": 2.1
}
],
"connections": {
"Wait 5s": {
"main": [
[
{
"node": "Notify Shortlisted Candidate",
"type": "main",
"index": 0
}
]
]
},
"Read All Rows": {
"main": [
[
{
"node": "Filter Unprocessed Rows",
"type": "main",
"index": 0
}
]
]
},
"Parse AI Output": {
"main": [
[
{
"node": "Build Sheet Request",
"type": "main",
"index": 0
}
]
]
},
"AI Resume Scorer": {
"main": [
[
{
"node": "Parse AI Output",
"type": "main",
"index": 0
}
]
]
},
"Update Sheet Row": {
"main": [
[
{
"node": "Check Score \u2265 7",
"type": "main",
"index": 0
}
]
]
},
"Check Score \u2265 7": {
"main": [
[
{
"node": "Alert HR Team",
"type": "main",
"index": 0
},
{
"node": "Wait 5s",
"type": "main",
"index": 0
}
],
[
{
"node": "Notify Rejected Candidate",
"type": "main",
"index": 0
}
]
]
},
"Build Sheet Request": {
"main": [
[
{
"node": "Update Sheet Row",
"type": "main",
"index": 0
}
]
]
},
"Google Sheets Trigger": {
"main": [
[
{
"node": "Read All Rows",
"type": "main",
"index": 0
}
]
]
},
"Filter Unprocessed Rows": {
"main": [
[
{
"node": "Extract Fields & Load JD",
"type": "main",
"index": 0
}
]
]
},
"Extract Fields & Load JD": {
"main": [
[
{
"node": "AI Resume Scorer",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Resume Scorer",
"type": "ai_languageModel",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automates your entire first-round resume screening process. When a candidate submits your Google Form application, the workflow triggers, extracts their details, scores them against the job description using AI, writes the results back to your Google Sheet, and…
Source: https://n8n.io/workflows/14138/ — 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 automation is designed to help you generate AI-powered music tracks, cover art, and fully rendered music videos — all triggered from a simple Telegram chat and managed via Google Sheets.
This workflow is designed for marketers, content creators, agencies, and solo founders who want to publish long‑form posts with visuals on autopilot using n8n and AI agents.
WhatsApp AI Assistant for Clinic Appointment Booking Automate your entire appointment lifecycle with an intelligent AI assistant that lives on WhatsApp. This workflow empowers any clinic or independen
Categories: Business Automation, Content Creation, SEO, AI
Streamline your recruitment process with AI-powered resume analysis that goes beyond keyword matching.