This workflow corresponds to n8n.io template #17165 — 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 →
{
"id": "WsgaLWQbatUHVqmC",
"meta": {
"builderVariant": "mcp",
"aiBuilderAssisted": true
},
"name": "Screen, score and route job applicants with Gemini, Gmail, Sheets and Slack",
"tags": [],
"nodes": [
{
"id": "2d5edc1a-4e62-4e04-9276-49cc24038160",
"name": "Schedule Daily Run",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
48,
48
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 8
}
]
}
},
"typeVersion": 1.3
},
{
"id": "4790bddd-069f-4168-a1c0-10fa7acb0538",
"name": "Get New Applicants",
"type": "n8n-nodes-base.googleSheets",
"position": [
272,
48
],
"parameters": {
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Applicants"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Select your spreadsheet"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "24623ae2-028b-4a4c-a1ba-137a7b7b5d15",
"name": "Set Job Requirements",
"type": "n8n-nodes-base.set",
"position": [
480,
48
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "role",
"name": "jobTitle",
"type": "string",
"value": "Senior Backend Engineer"
},
{
"id": "must",
"name": "mustHave",
"type": "string",
"value": "Python, cloud (AWS/GCP), 5+ years backend"
},
{
"id": "nice",
"name": "niceToHave",
"type": "string",
"value": "Team leadership, Kubernetes"
},
{
"id": "req",
"name": "requirements",
"type": "string",
"value": "Owns backend services end to end."
},
{
"id": "min",
"name": "minScore",
"type": "number",
"value": 70
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "fbf7e37f-bc25-409e-ab3e-5835304df83c",
"name": "Build Screening Prompt",
"type": "n8n-nodes-base.code",
"position": [
752,
48
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const cfg = $('Set Job Requirements').item.json;\nconst a = $json;\nconst name = a.Name||a.name||'';\nconst email = a.Email||a.email||'';\nconst resume = a.Resume||a.resume||a.ResumeText||a.experience||'';\nconst min = cfg.minScore||70;\nconst prompt = [\n 'You are a technical recruiter. Score the candidate against the role and respond ONLY with minified JSON:',\n '{\"score\":number,\"verdict\":\"strong\"|\"possible\"|\"weak\",\"strengths\":string[],\"gaps\":string[],\"red_flags\":string[],\"summary\":string}',\n 'verdict: strong if score>=' + min + ', possible if 50-' + (min-1) + ', otherwise weak.',\n '',\n 'Role: ' + (cfg.jobTitle||''),\n 'Must-have: ' + (cfg.mustHave||''),\n 'Nice-to-have: ' + (cfg.niceToHave||''),\n 'Requirements: ' + (cfg.requirements||''),\n '',\n 'Example -> {\"score\":82,\"verdict\":\"strong\",\"strengths\":[\"Python\",\"AWS\"],\"gaps\":[\"No GCP\"],\"red_flags\":[],\"summary\":\"Strong backend match\"}',\n '',\n 'Candidate: ' + name,\n 'Resume:',\n resume\n].join('\\n');\nreturn { prompt, name, email, role: cfg.jobTitle };"
},
"typeVersion": 2
},
{
"id": "fbe86081-847d-49ed-96bd-4fea8f9d6b7d",
"name": "Score Applicant with Gemini",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
976,
48
],
"parameters": {
"url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent",
"method": "POST",
"options": {},
"jsonBody": "={{ { \"contents\": [ { \"parts\": [ { \"text\": $json.prompt } ] } ], \"generationConfig\": { \"temperature\": 0.1, \"responseMimeType\": \"application/json\" } } }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "googlePalmApi"
},
"credentials": {
"googlePalmApi": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "688e036f-acc2-4f6b-bdc7-8319c2007c58",
"name": "Parse Score",
"type": "n8n-nodes-base.code",
"position": [
1280,
32
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const meta = $('Build Screening Prompt').item.json;\nlet text = '';\ntry { text = $json.candidates[0].content.parts[0].text; } catch (e) { text = ''; }\nlet d = {};\ntry { d = JSON.parse(text); } catch (e) { d = { score:0, verdict:'weak', strengths:[], gaps:[], red_flags:[], summary:(text||'').slice(0,200) }; }\nconst arr = (x) => Array.isArray(x) ? x.join('; ') : (x||'');\nreturn { name: meta.name, email: meta.email, role: meta.role, score: Number(d.score)||0, verdict: String(d.verdict||'weak').toLowerCase(), strengths: arr(d.strengths), gaps: arr(d.gaps), redFlags: arr(d.red_flags), summary: d.summary||'' };"
},
"typeVersion": 2
},
{
"id": "0790c265-ac64-4dc7-a9fa-13f4094b337e",
"name": "Update Applicant Row",
"type": "n8n-nodes-base.googleSheets",
"position": [
1616,
32
],
"parameters": {
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Applicants"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "Select your spreadsheet"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "c9563c0f-1f93-4400-9316-1dcc3b8dec7e",
"name": "Route by Verdict",
"type": "n8n-nodes-base.switch",
"position": [
2016,
0
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $(\"Parse Score\").item.json.verdict }}",
"rightValue": "strong"
}
]
}
},
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $(\"Parse Score\").item.json.verdict }}",
"rightValue": "possible"
}
]
}
},
{
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $(\"Parse Score\").item.json.verdict }}",
"rightValue": "weak"
}
]
}
}
]
},
"options": {
"fallbackOutput": "extra",
"renameFallbackOutput": "Other"
}
},
"typeVersion": 3.2
},
{
"id": "6ff1f43d-12c2-46aa-9e8a-d6a11f4b217f",
"name": "Notify Hiring Team",
"type": "n8n-nodes-base.slack",
"position": [
2240,
-16
],
"parameters": {
"text": "=*:star: Strong candidate for {{ $(\"Parse Score\").item.json.role }}*\n*Name:* {{ $(\"Parse Score\").item.json.name }}\n*Score:* {{ $(\"Parse Score\").item.json.score }}\n*Strengths:* {{ $(\"Parse Score\").item.json.strengths }}\n*Summary:* {{ $(\"Parse Score\").item.json.summary }}\nAn interview-invite draft is ready in Gmail.",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "#hiring"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
},
{
"id": "35829d1e-f814-4627-abca-0f6ef37a33fb",
"name": "Draft Interview Invite",
"type": "n8n-nodes-base.gmail",
"position": [
2464,
-16
],
"parameters": {
"message": "={{ \"Hi \" + $(\"Parse Score\").item.json.name + \",\\n\\nThanks for applying for the \" + $(\"Parse Score\").item.json.role + \" role. We were impressed by your background and would love to invite you to an interview. Please let us know your availability this week.\\n\\nBest regards\" }}",
"options": {
"sendTo": "={{ $(\"Parse Score\").item.json.email }}"
},
"subject": "={{ \"Interview invitation - \" + $(\"Parse Score\").item.json.role }}",
"resource": "draft"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "36cb1e27-cff5-4eed-b37f-6dff3d6e04ca",
"name": "Flag for Human Review",
"type": "n8n-nodes-base.slack",
"position": [
2240,
144
],
"parameters": {
"text": "=*:eyes: Borderline candidate - needs review*\n*Name:* {{ $(\"Parse Score\").item.json.name }}\n*Score:* {{ $(\"Parse Score\").item.json.score }}\n*Gaps:* {{ $(\"Parse Score\").item.json.gaps }}\n*Summary:* {{ $(\"Parse Score\").item.json.summary }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "#hiring-review"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
},
{
"id": "67e2a576-a22f-47c8-85d7-7c032304815b",
"name": "Draft Polite Rejection",
"type": "n8n-nodes-base.gmail",
"position": [
2240,
304
],
"parameters": {
"message": "={{ \"Hi \" + $(\"Parse Score\").item.json.name + \",\\n\\nThank you for your interest in the \" + $(\"Parse Score\").item.json.role + \" role. After careful review we have decided not to move forward at this time. We truly appreciate your interest and wish you the best in your search.\\n\\nBest regards\" }}",
"options": {
"sendTo": "={{ $(\"Parse Score\").item.json.email }}"
},
"subject": "={{ \"Your application for \" + $(\"Parse Score\").item.json.role }}",
"resource": "draft"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "6986e58d-af71-406f-b345-1d3039a45653",
"name": "Flag Unscored for Review",
"type": "n8n-nodes-base.slack",
"position": [
2240,
464
],
"parameters": {
"text": "=*:mag: Applicant could not be classified*\n*Name:* {{ $(\"Parse Score\").item.json.name }}\nPlease review manually.",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "#hiring-review"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
},
{
"id": "b4fb1461-cf97-4324-bfb5-f838859d775a",
"name": "Report Scoring Failure",
"type": "n8n-nodes-base.slack",
"position": [
1280,
208
],
"parameters": {
"text": "=*:x: Gemini scoring failed for {{ $(\"Build Screening Prompt\").item.json.name }}*\nPlease review this applicant manually.",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultName": "#hiring"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2.5
},
{
"id": "6c4fd580-b717-4385-b226-f76855cb1d84",
"name": "Overview Sticky",
"type": "n8n-nodes-base.stickyNote",
"position": [
-464,
-240
],
"parameters": {
"width": 440,
"height": 820,
"content": "## Screen, score and route job applicants with Gemini, Gmail, Sheets and Slack\n\n### How it works\nEach day the workflow reads only **new** applicants (rows without a Status) and asks Google Gemini to score them against your role, returning a score, verdict, strengths, gaps and red flags. The applicant row is updated with the result and marked Screened, so nobody is scored twice. A Switch then routes by verdict: strong candidates trigger a Slack alert plus an interview-invite draft, borderline ones are flagged for human review, weak ones get a polite rejection draft, and unscored ones are flagged. Scoring failures are reported to Slack.\n\n### Setup\n1. Connect Google Sheets, Google Gemini (PaLM) API, Gmail and Slack.\n2. Add applicants with columns Name, Email, Resume and an empty Status column.\n3. In **Set Job Requirements**, set the role, must-haves and minimum score.\n\n### Customization tips\nAdjust the score bands, add skills to weigh, or auto-send the drafts."
},
"typeVersion": 1
},
{
"id": "7cff5dc7-db84-456a-b78a-2e4a3f35b5d3",
"name": "S1",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
-240
],
"parameters": {
"color": 7,
"width": 672,
"height": 900,
"content": "## 1. Fetch new applicants & configure\nRead only rows without a Status (new applicants) and set the role requirements."
},
"typeVersion": 1
},
{
"id": "d99b188c-26e9-48f0-a02e-f91896595364",
"name": "S2",
"type": "n8n-nodes-base.stickyNote",
"position": [
688,
-240
],
"parameters": {
"color": 7,
"width": 800,
"height": 900,
"content": "## 2. AI scoring + error handling\nGemini scores each candidate as JSON. If the call fails, Slack is notified instead of dropping the applicant."
},
"typeVersion": 1
},
{
"id": "aa056e25-8e87-49ff-9b1a-301c9e5f7d9b",
"name": "S3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1504,
-240
],
"parameters": {
"color": 7,
"width": 364,
"height": 900,
"content": "## 3. Persist & de-duplicate\nUpdate the applicant row (match on Email) and set Status = Screened so they are never scored twice."
},
"typeVersion": 1
},
{
"id": "b6f66596-a800-4e65-aeea-2fa760f58ce0",
"name": "S4",
"type": "n8n-nodes-base.stickyNote",
"position": [
1888,
-240
],
"parameters": {
"color": 7,
"width": 880,
"height": 900,
"content": "## 4. Route by verdict & act\nStrong -> Slack alert + interview-invite draft. Possible -> human review. Weak -> rejection draft. Unscored -> manual review."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": true,
"executionOrder": "v1"
},
"versionId": "0c1cabc3-5437-4d25-8213-4747e432fe41",
"nodeGroups": [],
"connections": {
"Parse Score": {
"main": [
[
{
"node": "Update Applicant Row",
"type": "main",
"index": 0
}
]
]
},
"Route by Verdict": {
"main": [
[
{
"node": "Notify Hiring Team",
"type": "main",
"index": 0
}
],
[
{
"node": "Flag for Human Review",
"type": "main",
"index": 0
}
],
[
{
"node": "Draft Polite Rejection",
"type": "main",
"index": 0
}
],
[
{
"node": "Flag Unscored for Review",
"type": "main",
"index": 0
}
]
]
},
"Get New Applicants": {
"main": [
[
{
"node": "Set Job Requirements",
"type": "main",
"index": 0
}
]
]
},
"Notify Hiring Team": {
"main": [
[
{
"node": "Draft Interview Invite",
"type": "main",
"index": 0
}
]
]
},
"Schedule Daily Run": {
"main": [
[
{
"node": "Get New Applicants",
"type": "main",
"index": 0
}
]
]
},
"Set Job Requirements": {
"main": [
[
{
"node": "Build Screening Prompt",
"type": "main",
"index": 0
}
]
]
},
"Update Applicant Row": {
"main": [
[
{
"node": "Route by Verdict",
"type": "main",
"index": 0
}
]
]
},
"Build Screening Prompt": {
"main": [
[
{
"node": "Score Applicant with Gemini",
"type": "main",
"index": 0
}
]
]
},
"Score Applicant with Gemini": {
"main": [
[
{
"node": "Parse Score",
"type": "main",
"index": 0
}
],
[
{
"node": "Report Scoring 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.
gmailOAuth2googlePalmApigoogleSheetsOAuth2ApislackOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs daily to pull job applicants from Google Sheets, score each candidate against role requirements using the Google Gemini API, log results back to Google Sheets, and notify a Slack channel when an applicant meets or exceeds a minimum score. Runs every day at…
Source: https://n8n.io/workflows/17165/ — 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.
Imagine a dedicated financial expert tirelessly working behind the scenes, sifting through every transaction, every investment move, and every accounting entry. That's exactly what this automated syst
ChurnRadar B2B Alert Workflow. Uses httpRequest, openAi, slack, gmail. Scheduled trigger; 9 nodes.
Auto_Data_ML_Job_Application_v1. Uses httpRequest, openAi, gmail, googleSheets. Scheduled trigger; 7 nodes.
Auto_Data_ML_Job_Application_v1. Uses httpRequest, openAi, gmail, googleSheets. Scheduled trigger; 7 nodes.
This workflow runs twice daily to fetch recent Instagram post comments via the Instagram Graph API, uses OpenAI to classify sentiment, saves new positive comments to Google Sheets, and emails an HTML