This workflow follows the Executecommand → HTTP Request 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 →
{
"name": "2. AI Job Scorer (GPT-4o)",
"nodes": [
{
"parameters": {},
"id": "1",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
0,
0
]
},
{
"parameters": {
"resource": "databasePage",
"operation": "getAll",
"databaseId": {
"__rl": true,
"value": "34712819-3fc4-8199-8d57-db17c97631c7",
"mode": "id"
},
"returnAll": true,
"options": {
"simple": true
}
},
"id": "2",
"name": "Get Unscored Jobs",
"type": "n8n-nodes-base.notion",
"typeVersion": 2,
"position": [
200,
0
]
},
{
"parameters": {
"jsCode": "let results = [];\nfor (let i = 0; i < $input.all().length; i++) {\n const item = $input.all()[i];\n if (JSON.stringify(item.json).includes('Inbox')) {\n results.push({ json: item.json, pairedItem: { item: i } });\n }\n}\nreturn results;"
},
"id": "3",
"name": "Filter Inbox",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
400,
0
]
},
{
"parameters": {
"jsCode": "let results = [];\nfor (let i = 0; i < $input.all().length; i++) {\n const item = $input.all()[i];\n let jobTitle = 'Data Analyst';\n if (item.json['Job Title']) jobTitle = item.json['Job Title'];\n else if (item.json.property_job_title) jobTitle = item.json.property_job_title;\n else if (item.json.property_Job_Title) jobTitle = item.json.property_Job_Title;\n else if (item.json.properties?.['Job Title']?.title?.[0]) {\n jobTitle = item.json.properties['Job Title'].title[0].plain_text;\n }\n if (Array.isArray(jobTitle) && jobTitle[0]?.plain_text) jobTitle = jobTitle[0].plain_text;\n else if (typeof jobTitle === 'object' && jobTitle !== null && !Array.isArray(jobTitle) && jobTitle.plain_text) jobTitle = jobTitle.plain_text;\n else if (Array.isArray(jobTitle) && typeof jobTitle[0] === 'string') jobTitle = jobTitle[0];\n if (Array.isArray(jobTitle) && jobTitle.length === 0) jobTitle = 'Data Analyst';\n \n let location = '';\n if (item.json.Location) location = item.json.Location;\n else if (item.json.property_location) location = item.json.property_location;\n else if (item.json.property_Location) location = item.json.property_Location;\n else if (item.json.properties?.Location?.rich_text?.[0]) {\n location = item.json.properties.Location.rich_text[0].plain_text;\n }\n if (Array.isArray(location) && location[0]?.plain_text) location = location[0].plain_text;\n else if (typeof location === 'object' && location !== null && !Array.isArray(location) && location.plain_text) location = location.plain_text;\n else if (Array.isArray(location) && typeof location[0] === 'string') location = location[0];\n if (Array.isArray(location) && location.length === 0) location = '';\n \n let locLower = String(location || '').toLowerCase();\n let folder = '';\n if (locLower.includes('darwin') || locLower.includes('nt')) folder = 'Resumes_Dar';\n else if (locLower.includes('perth') || locLower.includes('wa')) folder = 'Resumes_Perth';\n else if (locLower.includes('brisbane') || locLower.includes('qld')) folder = 'Resumes_QLD';\n else if (locLower.includes('hobart') || locLower.includes('tas')) folder = 'Resumes_TAS';\n else if (locLower.includes('melbourne') || locLower.includes('vic')) folder = 'Resumes_Vic';\n \n if (folder === '') continue; // RESTORED DROPPING OF UNKNOWN STATES\n \n let cleanRationale = item.json.property_rationale || item.json.Rationale || item.json.property_Rationale || '';\n if (item.json.properties && item.json.properties.Rationale && item.json.properties.Rationale.rich_text && item.json.properties.Rationale.rich_text[0]) cleanRationale = item.json.properties.Rationale.rich_text[0].plain_text;\n if (Array.isArray(cleanRationale)) {\n if (cleanRationale[0] && cleanRationale[0].plain_text) cleanRationale = cleanRationale[0].plain_text;\n else if (typeof cleanRationale[0] === 'string') cleanRationale = cleanRationale[0];\n } else if (typeof cleanRationale === 'object' && cleanRationale !== null && cleanRationale.plain_text) cleanRationale = cleanRationale.plain_text;\n item.json.cleanRationale = String(cleanRationale);\n\n let titleLower = String(jobTitle || '').toLowerCase();\n let file = 'Prafful_Gadadasu_Software Engineer_CV.txt';\n if (titleLower.includes('c#') || titleLower.includes('.net')) file = 'Prafful_Gadadasu_C#_.Net_SQL_CV.txt';\n else if (titleLower.includes('full stack') || titleLower.includes('fullstack')) file = 'Prafful_Gadadasu_Full_Stack_.Net_SQL_CV.txt';\n else if (titleLower.includes('ict') || titleLower.includes('support')) file = 'Prafful_Gadadasu_ICT_CV.txt';\n else if (titleLower.includes('developer')) file = 'Prafful_Gadadasu_Software Developer_CV.txt';\n else if (titleLower.includes('data')) file = 'Prafful_Gadadasu_Data_Analyst_CV.txt';\n else if (titleLower.includes('engineer')) file = 'Prafful_Gadadasu_Software Engineer_CV.txt';\n \n item.json.ResumePath = `c:\\\\Users\\\\praff\\\\Documents\\\\Documents\\\\Important Docs\\\\Resumes\\\\${folder}\\\\${file}`;\n item.json.MappedResumeName = `${folder} / ${file.replace('.txt', '.docx')}`;\n item.json.cleanTitle = String(jobTitle);\n \n results.push({\n json: item.json,\n pairedItem: { item: i }\n });\n}\nreturn results;"
},
"id": "4",
"name": "Route Resume",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
600,
0
]
},
{
"parameters": {
"executeOnce": false,
"command": "={{ 'powershell.exe -NoProfile -Command \"Get-Content -Raw -Path \\'' + $json.ResumePath.replace(/\\\\/g, '\\\\\\\\') + '\\'\"' }}"
},
"id": "5",
"name": "Read Local File",
"type": "n8n-nodes-base.executeCommand",
"typeVersion": 1,
"position": [
800,
0
],
"continueOnFail": true
},
{
"parameters": {
"method": "POST",
"url": "https://api.openai.com/v1/chat/completions",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer <YOUR_OPENAI_API_KEY>"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "model",
"value": "gpt-4o"
},
{
"name": "messages",
"value": "={{ [{ role: 'system', content: 'You are an elite IT Recruiter. You must score this job listing strictly against the specific resume text provided. Give a match score from 0-100 based heavily on tech stack alignment. Output only a JSON object like {\\\"score\\\": 85, \\\"rationale\\\": \\\"Strong match because...\\\"}.' }, { role: 'user', content: 'Job Title: ' + $('Route Resume').item.json.cleanTitle + '\\nJob Details: ' + $('Route Resume').item.json.cleanRationale + '\\n\\nMY RESUME TEXT:\\n' + ($json.stdout || '') }] }}"
}
]
},
"options": {}
},
"id": "7",
"name": "Score with GPT-4o",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1000,
0
],
"continueOnFail": true
},
{
"parameters": {
"jsCode": "let results = [];\nfor (let i = 0; i < $input.all().length; i++) {\n let item = $input.all()[i];\n try {\n let content = item.json.choices[0].message.content || '';\n content = content.replace(/```json/g, '').replace(/```/g, '').trim();\n let parsed = JSON.parse(content);\n item.json.matchScore = parsed.score;\n item.json.rationale = parsed.rationale;\n results.push({ json: item.json, pairedItem: { item: i } });\n } catch (e) {\n item.json.matchScore = 0;\n let errMsg = 'OpenAI Error or Parsing Failed';\n if (item.json.error) errMsg = 'OpenAI Error: ' + JSON.stringify(item.json.error);\n else if (item.json.choices && item.json.choices[0]) errMsg = 'Parsing Failed: ' + item.json.choices[0].message.content;\n item.json.rationale = errMsg;\n results.push({ json: item.json, pairedItem: { item: i } });\n }\n}\nreturn results;"
},
"id": "8",
"name": "Parse Output",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1200,
0
]
},
{
"parameters": {
"resource": "databasePage",
"operation": "update",
"pageId": {
"__rl": true,
"value": "={{ $('Route Resume').item.json.id }}",
"mode": "id"
},
"propertiesUi": {
"propertyValues": [
{
"key": "Match Score|number",
"numberValue": "={{ $json.matchScore }}"
},
{
"key": "Rationale|rich_text",
"textContent": "={{ $json.rationale }}"
},
{
"key": "Status|select",
"selectValue": "Scored"
},
{
"key": "Resume|rich_text",
"textContent": "={{ $('Route Resume').item.json.MappedResumeName }}"
}
]
}
},
"id": "9",
"name": "Save Score",
"type": "n8n-nodes-base.notion",
"typeVersion": 2,
"position": [
1400,
0
]
}
],
"connections": {
"Manual Trigger": {
"main": [
[
{
"node": "Get Unscored Jobs",
"type": "main",
"index": 0
}
]
]
},
"Get Unscored Jobs": {
"main": [
[
{
"node": "Filter Inbox",
"type": "main",
"index": 0
}
]
]
},
"Filter Inbox": {
"main": [
[
{
"node": "Route Resume",
"type": "main",
"index": 0
}
]
]
},
"Route Resume": {
"main": [
[
{
"node": "Read Local File",
"type": "main",
"index": 0
}
]
]
},
"Read Local File": {
"main": [
[
{
"node": "Score with GPT-4o",
"type": "main",
"index": 0
}
]
]
},
"Score with GPT-4o": {
"main": [
[
{
"node": "Parse Output",
"type": "main",
"index": 0
}
]
]
},
"Parse Output": {
"main": [
[
{
"node": "Save Score",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
2. AI Job Scorer (GPT-4o). Uses notion, executeCommand, httpRequest. Event-driven trigger; 8 nodes.
Source: https://github.com/praffulgadadasu/AI-Job-Search-Automation/blob/19132ece6937904e1b87458e5bfcf0de37828f95/workflows/2-Scorer.json — 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.
Workflow 01.01. Uses notion, executeWorkflowTrigger, httpRequest. Event-driven trigger; 60 nodes.
Automate sales call analysis and store structured insights in Notion with AI-powered intelligence.
WorkFlow 01.02. Uses notion, httpRequest, executeWorkflowTrigger. Event-driven trigger; 27 nodes.
WorkFlow 05.01. Uses executeWorkflowTrigger, notion, httpRequest. Event-driven trigger; 26 nodes.
WorkFlow 05.02. Uses httpRequest, notion, executeWorkflowTrigger. Event-driven trigger; 26 nodes.