AutomationFlowsData & Sheets › AI Job Scorer (gpt-4o)

AI Job Scorer (gpt-4o)

2. AI Job Scorer (GPT-4o). Uses notion, executeCommand, httpRequest. Event-driven trigger; 8 nodes.

Event trigger★★★★☆ complexity8 nodesNotionExecute CommandHTTP Request
Data & Sheets Trigger: Event Nodes: 8 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "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
          }
        ]
      ]
    }
  }
}
Pro

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 →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

Workflow 01.01. Uses notion, executeWorkflowTrigger, httpRequest. Event-driven trigger; 60 nodes.

Notion, Execute Workflow Trigger, HTTP Request
Data & Sheets

Automate sales call analysis and store structured insights in Notion with AI-powered intelligence.

Execute Workflow Trigger, Notion, HTTP Request
Data & Sheets

WorkFlow 01.02. Uses notion, httpRequest, executeWorkflowTrigger. Event-driven trigger; 27 nodes.

Notion, HTTP Request, Execute Workflow Trigger
Data & Sheets

WorkFlow 05.01. Uses executeWorkflowTrigger, notion, httpRequest. Event-driven trigger; 26 nodes.

Execute Workflow Trigger, Notion, HTTP Request
Data & Sheets

WorkFlow 05.02. Uses httpRequest, notion, executeWorkflowTrigger. Event-driven trigger; 26 nodes.

HTTP Request, Notion, Execute Workflow Trigger