This workflow corresponds to n8n.io template #15293 — we link there as the canonical source.
This workflow follows the Chainllm → Gmail 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 →
{
"nodes": [
{
"id": "6ec9049c-50c0-4aee-81e8-9135a777aa2c",
"name": "Extract CV File ID",
"type": "n8n-nodes-base.code",
"position": [
-832,
464
],
"parameters": {
"jsCode": "const item = $input.first();\nconst json = item.json;\n\n// Get the CV URL from the form response\nconst cvUrl = json['CV / Resume Upload '] || '';\n\n// Extract Google Drive File ID from any URL format\nlet fileId = '';\n\nif (cvUrl) {\n // Format 1: https://drive.google.com/open?id=FILE_ID\n const openMatch = cvUrl.match(/[?&]id=([a-zA-Z0-9_-]+)/);\n\n // Format 2: https://drive.google.com/file/d/FILE_ID/view\n const fileMatch = cvUrl.match(/\\/file\\/d\\/([a-zA-Z0-9_-]+)/);\n\n // Format 3: https://docs.google.com/document/d/FILE_ID/edit\n const docsMatch = cvUrl.match(/\\/d\\/([a-zA-Z0-9_-]+)/);\n\n if (openMatch) {\n fileId = openMatch[1];\n } else if (fileMatch) {\n fileId = fileMatch[1];\n } else if (docsMatch) {\n fileId = docsMatch[1];\n }\n}\n\n// Return all original fields + extracted file ID\nreturn [\n {\n json: {\n ...json,\n cv_file_id: fileId\n }\n }\n];"
},
"typeVersion": 2
},
{
"id": "2b7700ee-18eb-47ff-9ea0-92982edc050c",
"name": "OpenRouter Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
960,
688
],
"parameters": {
"model": "openai/gpt-4o",
"options": {}
},
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "05ad1c2e-5417-4e7f-95b1-79bb6da0804b",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
1104,
688
],
"parameters": {
"jsonSchemaExample": "{\n \"score\": \"string\",\n \"seniority\": \"string\",\n \"recommendation\": \"string\",\n \"matched_skills\": \"string\",\n \"missing_skills\": \"string\",\n \"nice_to_have_matched\": \"string\",\n \"experience_comment\": \"string\",\n \"red_flags\": \"string\",\n \"summary\": \"string\"\n}"
},
"typeVersion": 1.3
},
{
"id": "177971c2-8659-4ca7-a556-4aa651b9bf9f",
"name": "Update in Sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1408,
464
],
"parameters": {
"columns": {
"value": {
"score": "={{ $json.output.score }}",
"summary": "={{ $json.output.summary }}",
"red_flags": "={{ $json.output.red_flags }}",
"seniority": "={{ $json.output.seniority }}",
"Email Address": "={{ $('Normalize Data').first().json.candidate_email }}",
"matched_skills": "={{ $json.output.matched_skills }}",
"missing_skills": "={{ $json.output.missing_skills }}",
"recommendation": "={{ $json.output.recommendation }}",
"experience_comment": "={{ $json.output.experience_comment }}",
"nice_to_have_matched": "={{ $json.output.nice_to_have_matched }}"
},
"schema": [
{
"id": "Timestamp",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Timestamp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email Address",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Email Address",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Full Name ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Full Name ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "WhatsApp / Phone Number ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "WhatsApp / Phone Number ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "City / Location ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "City / Location ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Role Applying For ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Role Applying For ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Years of Experience ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Years of Experience ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "CV / Resume Upload ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "CV / Resume Upload ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Anything you want us to know? (optional) ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Anything you want us to know? (optional) ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "score",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "seniority",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "seniority",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "recommendation",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "recommendation",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "matched_skills",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "matched_skills",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "missing_skills",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "missing_skills",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "nice_to_have_matched",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "nice_to_have_matched",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "experience_comment",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "experience_comment",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "red_flags",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "red_flags",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "summary",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "summary",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Column 19",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Column 19",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Column 20",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Column 20",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Column 21",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Column 21",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Email Address"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"cellFormat": "RAW"
},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 358702502,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1NBsshSSl7LVfn-TnP8JyhcoLTl7g0YTsvrpMZygN670/edit#gid=358702502",
"cachedResultName": "Form Responses 1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1NBsshSSl7LVfn-TnP8JyhcoLTl7g0YTsvrpMZygN670",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1NBsshSSl7LVfn-TnP8JyhcoLTl7g0YTsvrpMZygN670/edit?usp=drivesdk",
"cachedResultName": "Athena Jobs"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "c2e135f9-42f2-4c3c-9015-4dd580abb62e",
"name": "Job Profile",
"type": "n8n-nodes-base.set",
"position": [
64,
176
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "role_title",
"name": "role_title",
"type": "string",
"value": "n8n Automation Developer"
},
{
"id": "required_skills",
"name": "required_skills",
"type": "string",
"value": "n8n, API integrations, Webhooks, HTTP Request node, JSON handling, Google Sheets, expressions, workflow logic, error handling"
},
{
"id": "nice_to_have_skills",
"name": "nice_to_have_skills",
"type": "string",
"value": "Make.com, Zapier, Python, AI agents, LangChain, OpenAI API, Airtable, CRM integrations"
},
{
"id": "min_years_required",
"name": "min_years_required",
"type": "string",
"value": "2"
},
{
"id": "junior_threshold",
"name": "junior_threshold",
"type": "string",
"value": "Less than 1 year, 1 \u2013 2 years"
},
{
"id": "mid_threshold",
"name": "mid_threshold",
"type": "string",
"value": "2 \u2013 4 years"
},
{
"id": "senior_threshold",
"name": "senior_threshold",
"type": "string",
"value": "4 \u2013 7 years, 7+ years"
},
{
"id": "tech_weight",
"name": "tech_weight",
"type": "string",
"value": "60"
},
{
"id": "experience_weight",
"name": "experience_weight",
"type": "string",
"value": "25"
},
{
"id": "nice_to_have_weight",
"name": "nice_to_have_weight",
"type": "string",
"value": "15"
},
{
"id": "job_context",
"name": "job_context",
"type": "string",
"value": "Agency-side automation role. Candidate must build client workflows independently, handle API connections, and deliver production-ready automations with documentation."
}
]
}
},
"typeVersion": 3.4
},
{
"id": "301585e3-8e61-4ef3-b8f9-fd06c550c4a7",
"name": "Job Profile1",
"type": "n8n-nodes-base.set",
"position": [
64,
368
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "role_title",
"name": "role_title",
"type": "string",
"value": "React Developer"
},
{
"id": "required_skills",
"name": "required_skills",
"type": "string",
"value": "React.js, JavaScript, TypeScript, REST APIs, Git, CSS, component architecture, state management, responsive design"
},
{
"id": "nice_to_have_skills",
"name": "nice_to_have_skills",
"type": "string",
"value": "Next.js, GraphQL, Tailwind CSS, Redux, React Query, Figma to code, unit testing, Vercel deployment"
},
{
"id": "min_years_required",
"name": "min_years_required",
"type": "string",
"value": "2"
},
{
"id": "junior_threshold",
"name": "junior_threshold",
"type": "string",
"value": "Less than 1 year, 1 \u2013 2 years"
},
{
"id": "mid_threshold",
"name": "mid_threshold",
"type": "string",
"value": "2 \u2013 4 years"
},
{
"id": "senior_threshold",
"name": "senior_threshold",
"type": "string",
"value": "4 \u2013 7 years, 7+ years"
},
{
"id": "tech_weight",
"name": "tech_weight",
"type": "string",
"value": "65"
},
{
"id": "experience_weight",
"name": "experience_weight",
"type": "string",
"value": "20"
},
{
"id": "nice_to_have_weight",
"name": "nice_to_have_weight",
"type": "string",
"value": "15"
},
{
"id": "job_context",
"name": "job_context",
"type": "string",
"value": "Agency-side frontend role. Candidate builds client-facing web apps, works from Figma designs, and must deliver clean reusable components with minimal supervision."
}
]
}
},
"typeVersion": 3.4
},
{
"id": "59237f3c-7a48-4ec6-ad3b-cadf596c8037",
"name": "Job Profile2",
"type": "n8n-nodes-base.set",
"position": [
64,
560
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "role_title",
"name": "role_title",
"type": "string",
"value": "SEO Expert"
},
{
"id": "required_skills",
"name": "required_skills",
"type": "string",
"value": "On-page SEO, Technical SEO, keyword research, backlink building, Google Search Console, Google Analytics, SEMrush or Ahrefs, content strategy, site audit"
},
{
"id": "nice_to_have_skills",
"name": "nice_to_have_skills",
"type": "string",
"value": "Local SEO, schema markup, WordPress SEO, Screaming Frog, link outreach, Core Web Vitals, YouTube SEO"
},
{
"id": "min_years_required",
"name": "min_years_required",
"type": "string",
"value": "2"
},
{
"id": "junior_threshold",
"name": "junior_threshold",
"type": "string",
"value": "Less than 1 year, 1 \u2013 2 years"
},
{
"id": "mid_threshold",
"name": "mid_threshold",
"type": "string",
"value": "2 \u2013 4 years"
},
{
"id": "senior_threshold",
"name": "senior_threshold",
"type": "string",
"value": "4 \u2013 7 years, 7+ years"
},
{
"id": "tech_weight",
"name": "tech_weight",
"type": "string",
"value": "55"
},
{
"id": "experience_weight",
"name": "experience_weight",
"type": "string",
"value": "30"
},
{
"id": "nice_to_have_weight",
"name": "nice_to_have_weight",
"type": "string",
"value": "15"
},
{
"id": "job_context",
"name": "job_context",
"type": "string",
"value": "Agency SEO role managing multiple client websites. Candidate must handle audits, reporting, and strategy independently. Client-facing communication required."
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a0446724-ae5f-4c91-9cc9-2b61bf8b72d3",
"name": "Job Profile3",
"type": "n8n-nodes-base.set",
"position": [
64,
752
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "role_title",
"name": "role_title",
"type": "string",
"value": "Meta Ads Expert"
},
{
"id": "required_skills",
"name": "required_skills",
"type": "string",
"value": "Facebook Ads Manager, campaign structure, audience targeting, retargeting, pixel setup, A/B testing, ad copywriting, budget management, ROAS optimization, reporting"
},
{
"id": "nice_to_have_skills",
"name": "nice_to_have_skills",
"type": "string",
"value": "Instagram Ads, WhatsApp Ads, Looker Studio, Google Tag Manager, landing page CRO, Klaviyo, ecommerce ads experience"
},
{
"id": "min_years_required",
"name": "min_years_required",
"type": "string",
"value": "2"
},
{
"id": "junior_threshold",
"name": "junior_threshold",
"type": "string",
"value": "Less than 1 year, 1 \u2013 2 years"
},
{
"id": "mid_threshold",
"name": "mid_threshold",
"type": "string",
"value": "2 \u2013 4 years"
},
{
"id": "senior_threshold",
"name": "senior_threshold",
"type": "string",
"value": "4 \u2013 7 years, 7+ years"
},
{
"id": "tech_weight",
"name": "tech_weight",
"type": "string",
"value": "55"
},
{
"id": "experience_weight",
"name": "experience_weight",
"type": "string",
"value": "30"
},
{
"id": "nice_to_have_weight",
"name": "nice_to_have_weight",
"type": "string",
"value": "15"
},
{
"id": "job_context",
"name": "job_context",
"type": "string",
"value": "Agency paid media role handling multiple client ad accounts. Candidate must manage budgets, optimize campaigns, and deliver monthly performance reports independently."
}
]
}
},
"typeVersion": 3.4
},
{
"id": "858f35c9-6e11-48ab-a6e2-f855f43fbde7",
"name": "New Application Received",
"type": "n8n-nodes-base.googleSheetsTrigger",
"position": [
-1056,
464
],
"parameters": {
"event": "rowAdded",
"options": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 358702502,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1NBsshSSl7LVfn-TnP8JyhcoLTl7g0YTsvrpMZygN670/edit#gid=358702502",
"cachedResultName": "Form Responses 1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1NBsshSSl7LVfn-TnP8JyhcoLTl7g0YTsvrpMZygN670",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1NBsshSSl7LVfn-TnP8JyhcoLTl7g0YTsvrpMZygN670/edit?usp=drivesdk",
"cachedResultName": "Athena Jobs"
}
},
"credentials": {
"googleSheetsTriggerOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "6801095a-2f6d-429e-a8fb-2cf38cc3480a",
"name": "Normalize Data",
"type": "n8n-nodes-base.set",
"position": [
336,
464
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "pass_role_title",
"name": "role_title",
"type": "string",
"value": "={{ $json.role_title }}"
},
{
"id": "pass_required_skills",
"name": "required_skills",
"type": "string",
"value": "={{ $json.required_skills }}"
},
{
"id": "pass_nice_to_have_skills",
"name": "nice_to_have_skills",
"type": "string",
"value": "={{ $json.nice_to_have_skills }}"
},
{
"id": "pass_min_years_required",
"name": "min_years_required",
"type": "string",
"value": "={{ $json.min_years_required }}"
},
{
"id": "pass_junior_threshold",
"name": "junior_threshold",
"type": "string",
"value": "={{ $json.junior_threshold }}"
},
{
"id": "pass_mid_threshold",
"name": "mid_threshold",
"type": "string",
"value": "={{ $json.mid_threshold }}"
},
{
"id": "pass_senior_threshold",
"name": "senior_threshold",
"type": "string",
"value": "={{ $json.senior_threshold }}"
},
{
"id": "pass_tech_weight",
"name": "tech_weight",
"type": "string",
"value": "={{ $json.tech_weight }}"
},
{
"id": "pass_experience_weight",
"name": "experience_weight",
"type": "string",
"value": "={{ $json.experience_weight }}"
},
{
"id": "pass_nice_to_have_weight",
"name": "nice_to_have_weight",
"type": "string",
"value": "={{ $json.nice_to_have_weight }}"
},
{
"id": "pass_job_context",
"name": "job_context",
"type": "string",
"value": "={{ $json.job_context }}"
},
{
"id": "pass_cv_url",
"name": "cv_file_id",
"type": "string",
"value": "={{ $('Extract CV File ID').item.json.cv_file_id }}"
},
{
"id": "pass_candidate_name",
"name": "candidate_name",
"type": "string",
"value": "={{ $('Extract CV File ID').item.json['Full Name '] }}"
},
{
"id": "pass_candidate_email",
"name": "candidate_email",
"type": "string",
"value": "={{ $('Extract CV File ID').item.json['Email Address'] }}"
},
{
"id": "pass_candidate_phone",
"name": "candidate_phone",
"type": "string",
"value": "={{ $('Extract CV File ID').item.json['WhatsApp / Phone Number '] }}"
},
{
"id": "pass_candidate_city",
"name": "candidate_address",
"type": "string",
"value": "={{ $('Extract CV File ID').item.json['City / Location '] }}"
},
{
"id": "pass_years_experience",
"name": "candidate_years_experience",
"type": "string",
"value": "={{ $('Extract CV File ID').item.json['Years of Experience '] }}"
},
{
"id": "pass_cover_note",
"name": "cover_note",
"type": "string",
"value": "={{ $('Extract CV File ID').item.json['Anything you want us to know? (optional) '] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "1954852b-f201-484f-9bc0-371487631f8f",
"name": "Download CV from Drive",
"type": "n8n-nodes-base.googleDrive",
"position": [
544,
464
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.cv_file_id }}"
},
"options": {},
"operation": "download"
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "e6e89fdc-9d53-40be-8dd5-a186ab94b7af",
"name": "Extract CV Text",
"type": "n8n-nodes-base.extractFromFile",
"position": [
736,
464
],
"parameters": {
"options": {},
"operation": "pdf"
},
"typeVersion": 1.1
},
{
"id": "6fbdcdbc-4afd-46ea-be9b-0ad0a91fea05",
"name": "Evaluate Candidate",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
960,
464
],
"parameters": {
"text": "=Evaluate this candidate for the following role and return your assessment as a JSON object.\n\n=== JOB PROFILE ===\nRole: {{ $('Normalize Data').item.json.role_title }}\nRequired Skills: {{ $('Normalize Data').item.json.required_skills }}\nNice-to-Have Skills: {{ $('Normalize Data').item.json.nice_to_have_skills }}\nMinimum Years Required: {{ $('Normalize Data').item.json.min_years_required }}\nSeniority Bands \u2014 Junior: {{ $('Normalize Data').item.json.junior_threshold }} | Mid: {{ $('Normalize Data').item.json.mid_threshold }} | Senior: {{ $('Normalize Data').item.json.senior_threshold }}\nScoring Weights \u2014 Technical: {{ $('Normalize Data').item.json.tech_weight }}% | Experience: {{ $('Normalize Data').item.json.experience_weight }}% | Nice-to-Have: {{ $('Normalize Data').item.json.nice_to_have_weight }}%\nJob Context: {{ $('Normalize Data').item.json.job_context }}\n\n=== CANDIDATE ===\nName: {{ $('Normalize Data').item.json.candidate_name }}\nDeclared Experience: {{ $('Normalize Data').item.json.candidate_years_experience }}\nLocation: {{ $('Normalize Data').item.json.candidate_address }}\nCover Note: {{ $('Normalize Data').item.json.cover_note || 'None' }}\n\n=== CV TEXT ===\n{{ $json.text }}",
"batching": {},
"messages": {
"messageValues": [
{
"message": "=You are a senior recruitment analyst at a digital staffing agency. You evaluate candidate CVs against specific job profiles and produce objective, evidence-based assessments that hiring managers use to make interview decisions.\n\nEVALUATION RULES:\n\n1. SCORING METHOD (score out of 10):\nYou must calculate the score using a weighted formula:\n- Technical Skills Score \u00d7 {{ $('Normalize Data').item.json.tech_weight }}%\n- Experience Score \u00d7 {{ $('Normalize Data').item.json.experience_weight }}%\n- Nice-to-Have Skills Score \u00d7 {{ $('Normalize Data').item.json.nice_to_have_weight }}%\n\nScore each component from 0\u201310 before applying the weight. Combine for the final score. Round to one decimal place.\n\n2. SENIORITY DETERMINATION:\nAssign seniority based on the following thresholds for this role:\n- Junior: {{ $('Normalize Data').item.json.junior_threshold }}\n- Mid: {{ $('Normalize Data').item.json.mid_threshold }}\n- Senior: {{ $('Normalize Data').item.json.senior_threshold }}\n\nAlways cross-check the candidate's declared years against actual CV evidence. If the CV shows less real experience than declared, downgrade the seniority. If the CV shows stronger evidence than declared years suggest, you may upgrade.\n\n3. RECOMMENDATION LOGIC:\n- Interview \u2192 final score 7.0 or above AND no critical required skills missing\n- Maybe \u2192 final score 5.0 to 6.9 OR one or two required skills missing but strong in others\n- Reject \u2192 final score below 5.0 OR more than half of required skills absent from CV\n\n4. RED FLAG DETECTION:\nFlag any of the following if present:\n- Required skills listed by candidate but no project or employer evidence found in CV\n- Years of experience declared significantly exceeds what CV timeline supports\n- CV contains no measurable outcomes, no tool names, no real project detail\n- Employment gaps longer than 6 months with no explanation\n- CV is clearly generic or template-filled with no role-specific content\n\n5. OUTPUT RULES:\n- Return ONLY the JSON object. No text before it. No text after it. No markdown fences.\n- matched_skills must only list items from the required skills list that appear with evidence in the CV\n- missing_skills must only list required skills not evidenced in the CV\n- nice_to_have_matched must only list items from the nice-to-have list found in the CV\n- red_flags must be \"None\" if no flags found\n- All values must be plain strings \u2014 no nested objects, no arrays, no markdown\n\nOutput Should be:\n\n{\n \"score\": \"string\",\n \"seniority\": \"string\",\n \"recommendation\": \"string\",\n \"matched_skills\": \"string\",\n \"missing_skills\": \"string\",\n \"nice_to_have_matched\": \"string\",\n \"experience_comment\": \"string\",\n \"red_flags\": \"string\",\n \"summary\": \"string\"\n}\n\n\nReturn only the raw JSON object. No markdown. No backticks. No explanation before or after."
}
]
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.9
},
{
"id": "0cddb053-6f41-4f52-a497-029f87d8303a",
"name": "Route by Job Role",
"type": "n8n-nodes-base.switch",
"position": [
-160,
432
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "bfcb95a0-1371-46bc-8879-43dbb05e5488",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Extract CV File ID').item.json['Role Applying For '] }}",
"rightValue": "n8n Automation Developer"
}
]
}
},
{
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "dbdc3971-9762-45d9-8fc2-d106025e87fe",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Extract CV File ID').item.json['Role Applying For '] }}",
"rightValue": "React Developer"
}
]
}
},
{
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "7954fdf0-2999-4f7f-a71c-a4a35c3fd3d4",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Extract CV File ID').item.json['Role Applying For '] }}",
"rightValue": "SEO Expert"
}
]
}
},
{
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "216d94db-e4e3-41d0-97d4-f206280b9d83",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Extract CV File ID').item.json['Role Applying For '] }}",
"rightValue": "Meta Ads Expert"
}
]
}
}
]
},
"options": {}
},
"typeVersion": 3.4
},
{
"id": "e242cb57-8b83-47ff-8e56-0ae83978aa89",
"name": "Is Email Unique?",
"type": "n8n-nodes-base.dataTable",
"position": [
-608,
464
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "Email",
"keyValue": "={{ $json['Email Address'] }}"
}
]
},
"operation": "rowNotExists",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "TZXVS8Ac8Nn1q0jM",
"cachedResultUrl": "/projects/UixkftX5yd6UCJ81/datatables/TZXVS8Ac8Nn1q0jM",
"cachedResultName": "CV Screening"
}
},
"typeVersion": 1.1
},
{
"id": "4279cdff-b8e9-41cc-a1e0-59b0572162d8",
"name": "Add Email to Table",
"type": "n8n-nodes-base.dataTable",
"position": [
-384,
464
],
"parameters": {
"columns": {
"value": {
"Email": "={{ $json['Email Address'] }}"
},
"schema": [
{
"id": "Email",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Email",
"defaultMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Email"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "TZXVS8Ac8Nn1q0jM",
"cachedResultUrl": "/projects/UixkftX5yd6UCJ81/datatables/TZXVS8Ac8Nn1q0jM",
"cachedResultName": "CV Screening"
}
},
"typeVersion": 1.1
},
{
"id": "5929d1e3-4c22-40d2-9b36-fc9a2639f59f",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1104,
288
],
"parameters": {
"color": 7,
"width": 416,
"height": 352,
"content": "## \ud83d\udce5 Application Entry\n\nTriggers every minute when a new row is added to the \nGoogle Form responses sheet. Extracts the Google Drive \nfile ID from the CV upload URL \u2014 handles all 3 Drive \nURL formats automatically."
},
"typeVersion": 1
},
{
"id": "c43ed779-d2c5-4a38-809d-b9ff08199efe",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-656,
288
],
"parameters": {
"color": 7,
"width": 416,
"height": 352,
"content": "## \ud83d\udee1\ufe0f Duplicate Guard\n\nChecks the n8n internal Data Table to see if this \nemail has already been processed. If the email exists, \nthe workflow stops \u2014 preventing duplicate evaluations. \nIf new, the email is logged to the table before \ncontinuing."
},
"typeVersion": 1
},
{
"id": "03c9c93f-4633-4916-8be3-795685102d3c",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-224,
-48
],
"parameters": {
"color": 7,
"width": 496,
"height": 944,
"content": "## \u2699\ufe0f Role-Based Job Profiles\n\nRoutes the candidate to the correct job profile based \non the role they selected in the form. Each profile \ndefines the required skills, nice-to-have skills, \nminimum experience, seniority thresholds, and weighted \nscoring criteria for that specific role.\n\n\u26a0\ufe0f\u2192 Customize each Job Profile node to match your \n agency's hiring requirements."
},
"typeVersion": 1
},
{
"id": "5746fd63-364d-4d20-b621-0218fdc08a2b",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
288,
176
],
"parameters": {
"color": 7,
"width": 208,
"height": 464,
"content": "## \ud83d\uddc2\ufe0f Normalize Candidate Data\n\nMerges all 4 job profile branches into a single clean \ndata object. Standardizes messy Google Form field names \n(with trailing spaces) into clean keys used by all \ndownstream nodes."
},
"typeVersion": 1
},
{
"id": "b35c9f53-8fc7-446d-a144-b715815e3191",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
512,
304
],
"parameters": {
"color": 7,
"width": 352,
"height": 336,
"content": "## \ud83d\udcc4 CV Extraction\n\nDownloads the candidate's CV from Google Drive using \nthe extracted file ID. Converts the PDF into plain \ntext so the AI model can read and evaluate it."
},
"typeVersion": 1
},
{
"id": "77bce68b-0759-4663-858a-bc5627c3126a",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
1312,
256
],
"parameters": {
"color": 7,
"width": 352,
"height": 336,
"content": "## \ud83d\udcca Results Logged to Sheet\n\nWrites all AI evaluation results back to the same \nGoogle Form responses sheet, matched by email address. \nHR can now see score, seniority, and recommendation \ndirectly alongside the original application row."
},
"typeVersion": 1
},
{
"id": "76ae9eb5-86ca-4529-87e1-4820a0695d12",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
880,
192
],
"parameters": {
"color": 7,
"width": 400,
"height": 640,
"content": "## \ud83e\udd16 AI Evaluation Engine\n\nSends the CV text and job profile to the AI model via \na dynamically built prompt. The system prompt and \nscoring weights are pulled from the job profile \u2014 no \nhardcoding. Returns a structured JSON with:\n\nscore \u00b7 seniority \u00b7 recommendation \u00b7 matched_skills \nmissing_skills \u00b7 nice_to_have_matched \u00b7 red_flags \u00b7 summary\n\n\u2192 Change the model in OpenRouter Chat Model node.\n Default: openrouter/free (for testing).\n Switch to gpt-4o or deepseek for production."
},
"typeVersion": 1
},
{
"id": "c10f74fc-85a0-489b-9807-8a8aa216b743",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1120,
-96
],
"parameters": {
"color": 7,
"width": 2800,
"height": 1008,
"content": "# #1 HR Decision & Email Sender"
},
"typeVersion": 1
},
{
"id": "ecb3470d-b5ad-4255-901f-cb8669bd8f61",
"name": "Google Sheets Trigger",
"type": "n8n-nodes-base.googleSheetsTrigger",
"position": [
-1040,
1344
],
"parameters": {
"event": "rowUpdate",
"options": {
"columnsToWatch": [
"HR_Decision"
]
},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 358702502,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1NBsshSSl7LVfn-TnP8JyhcoLTl7g0YTsvrpMZygN670/edit#gid=358702502",
"cachedResultName": "Form Responses 1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1NBsshSSl7LVfn-TnP8JyhcoLTl7g0YTsvrpMZygN670",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1NBsshSSl7LVfn-TnP8JyhcoLTl7g0YTsvrpMZygN670/edit?usp=drivesdk",
"cachedResultName": "Athena Jobs"
}
},
"credentials": {
"googleSheetsTriggerOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "5208016e-d6f2-4bc6-be97-f5104956137a",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
-864,
1344
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "5ba69e88-d63c-450a-9ddb-3e24c4de8ba7",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.HR_Decision }}",
"rightValue": ""
},
{
"id": "fd9c7b07-78a3-4a9c-b818-b0ed8b266bcf",
"operator": {
"type": "string",
"operation": "empty",
"singleValue": true
},
"leftValue": "={{ $json.Email_Status }}",
"rightValue": "Sent"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "c0a174d6-ecab-4c40-9e18-aa4c66e5793b",
"name": "Switch",
"type": "n8n-nodes-base.switch",
"position": [
-640,
1328
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "a5f89624-230b-4e91-b41b-ff8a71ac3c0a",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.HR_Decision }}",
"rightValue": "Send Invite"
}
]
}
},
{
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "57a1f8e7-7219-45cf-8147-5e3a76636f1d",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.HR_Decision }}",
"rightValue": "Send Rejection"
}
]
}
}
]
},
"options": {}
},
"typeVersion": 3.4
},
{
"id": "9e991a30-b03a-421b-a355-d5de41bec1e2",
"name": "Invitation Message",
"type": "n8n-nodes-base.set",
"position": [
-336,
1248
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "492c2fb2-c67c-4f79-851d-862efa29e81c",
"name": "email",
"type": "string",
"value": "={{ $json['Email Address'] }}"
},
{
"id": "d3ffedf4-b56c-46d2-82ef-bb52f638cae8",
"name": "message",
"type": "string",
"value": "=Hi {{ $json['Full Name '] }},\n\nThank you for applying for the {{ $json['Role Applying For '] }} position. We reviewed your CV and were very impressed with your background. We would love to invite you to an interview!\n\nPlease select a time that works best for you using the link below. For your convenience, your name and email are already pre-filled:\n\nyour-calandly-link-here?email={{ $json['Email Address'] }}&name={{ $json['Full Name '] }}\n\nWe look forward to speaking with you soon!\n\nBest regards,\nThe Hiring Team"
},
{
"id": "e82d37c4-3b8e-4fad-8c4f-0b64655c70dc",
"name": "subject",
"type": "string",
"value": "=Invitation to Interview: {{ $json['Role Applying For '] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "dab4137e-e115-4bdb-9cd9-c07826b5753b",
"name": "Rejection Message",
"type": "n8n-nodes-base.set",
"position": [
-336,
1488
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "492c2fb2-c67c-4f79-851d-862efa29e81c",
"name": "email",
"type": "string",
"value": "={{ $json['Email Address'] }}"
},
{
"id": "d3ffedf4-b56c-46d2-82ef-bb52f638cae8",
"name": "message",
"type": "string",
"value": "=Hi {{ $json['Full Name '] }},\n\nThank you so much for taking the time to share your CV with us for the {{ $json['Role Applying For '] }} position. We truly appreciate the effort you put into your application.\n\nAfter careful review, we have decided to move forward with other candidates whose profiles more closely match our exact project needs at this moment. Please know that this was a difficult decision, as we saw some great qualities and potential in your background!\n\nThe tech and digital space is incredibly vast, and your passion for this field is clear. We highly encourage you to keep building your skills, working on exciting new projects, and pushing your boundaries. Every application is just a stepping stone leading you to the perfect opportunity. \n\nKeep up the great work, and don't stop building! We wish you nothing but the absolute best in your career journey and hope our paths might cross again in the future.\n\nWarmly,\nThe Hiring Team"
},
{
"id": "e82d37c4-3b8e-4fad-8c4f-0b64655c70dc",
"name": "subject",
"type": "string",
"value": "=Update on your application for {{ $json['Role Applying For '] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "17bb7d67-84cd-49a3-9306-dcd96473a9a6",
"name": "Send Interview Invite",
"type": "n8n-nodes-base.gmail",
"position": [
-96,
1248
],
"parameters": {
"sendTo": "={{ $json.email }}",
"message": "={{ $json.message }}",
"options": {},
"subject": "={{ $json.subject }}",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"executeOnce": true,
"typeVersion": 2.2
},
{
"id": "c28b9a9a-9b78-4a69-9789-9956083504ac",
"name": "Send Rejection Email",
"type": "n8n-nodes-base.gmail",
"position": [
-80,
1488
],
"parameters": {
"sendTo": "={{ $json.email }}",
"message": "={{ $json.message }}",
"options": {},
"subject": "={{ $json.subject }}",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"executeOnce": true,
"typeVersion": 2.2
},
{
"id": "6e145947-1d4f-417c-8f71-e301ac5edbab",
"name": "Mark Invite Sent",
"type": "n8n-nodes-base.googleSheets",
"position": [
80,
1248
],
"parameters": {
"columns": {
"value": {
"Email_Status": "Sent",
"Email Address": "={{ $('Invitation Message').item.json.email }}"
},
"schema": [
{
"id": "Timestamp",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Timestamp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email Address",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Email Address",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Full Name ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Full Name ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "WhatsApp / Phone Number ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "WhatsApp / Phone Number ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "City / Location ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "City / Location ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Role Applying For ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Role Applying For ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Years of Experience ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Years of Experience ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "CV / Resume Upload ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "CV / Resume Upload ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Anything you want us to know? (optional) ",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Anything you want us to know? (optional) ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "score",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "seniority",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "seniority",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "recommendation",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "recommendation",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "matched_skills",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "matched_skills",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "missing_skills",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "missing_skills",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "nice_to_have_matched",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "nice_to_have_matched",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "experience_comment",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "experience_comment",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "red_flags",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "red_flags",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "summary",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "summary",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "HR_Decision",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "HR_Decision",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email_Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Email_Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Interview_Time",
"type": "string",
"display": true,
"removed": true,
"required": false,
"displayName": "Interview_Time",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Email Address"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 358702502,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1NBsshSSl7LVfn-TnP8JyhcoLTl7g0YTsvrpMZygN670/edit#gid
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.
calendlyOAuth2ApigmailOAuth2googleDriveOAuth2ApigoogleSheetsOAuth2ApigoogleSheetsTriggerOAuth2ApiopenRouterApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This template automates the complete hiring pipeline for digital agencies managing applications across multiple job roles. When a candidate submits a Google Form with their CV, the system scores it with AI against a role-specific weighted job profile, lets HR decide in Google…
Source: https://n8n.io/workflows/15293/ — 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 is ideal for HR teams, startup founders, operations leads, remote-first companies, and freelancers managing onboarding manually or across multiple tools.
n8n Graphic Design Team. Uses googleSheets, googleDrive, httpRequest, outputParserStructured. Event-driven trigger; 37 nodes.
The automation loads rows from a Google Sheet of leads that you want to contact. It makes a Google search via Apify for LinkedIn links based on the First name / Last name / Company. Another Apify acto
Wait Splitout. Uses lmOpenAi, outputParserStructured, httpRequest, stickyNote. Event-driven trigger; 26 nodes.
Wait Splitout. Uses lmOpenAi, outputParserStructured, httpRequest, stickyNote. Event-driven trigger; 26 nodes.