Automate Resume Screening with n8n + Ollama (AI Recruitment Pipeline)

Published March 24, 2026 · 11 min read

Recruiters spend 23 hours screening resumes for a single hire. For popular positions, that means reading 200–500 applications, most of which are obvious non-matches. AI resume screening tools like HireVue or Pymetrics charge $5,000–25,000/year and require uploading candidate data to cloud servers.

With n8n and Ollama, you can build a resume screening pipeline that runs on your own hardware — zero API costs, candidate data stays private (critical for GDPR), and you control exactly how scoring works.

In this tutorial, you'll build a workflow that:

  1. Receives resumes via email attachment or webhook upload
  2. Extracts text from PDF/DOCX documents
  3. Scores candidates against your job requirements using local AI
  4. Extracts structured data (skills, experience, education)
  5. Ranks applicants and sends a shortlist to the hiring manager

Why Local AI for Resume Screening?

Resume screening is a perfect use case for self-hosted AI:

FactorCloud AI ScreeningLocal AI (Ollama)
Data privacy (GDPR)Candidate PII sent to US serversData never leaves your infrastructure
Cost per resume$0.05–0.50 per API call$0 (runs on your hardware)
Bias auditabilityBlack box — can't inspect modelFull control over prompts and scoring
CustomizationGeneric scoring modelsTailored to your exact job requirements
Volume limitsRate-limitedUnlimited (hardware-bound only)

GDPR note: Under GDPR Article 22, candidates have the right to not be subject to purely automated decision-making. This workflow is designed as a screening assistant — it ranks and scores to help humans make faster decisions, not to auto-reject candidates. Always have a human make the final hiring decision.

The Architecture

Resume Received (Email/Webhook)
    ↓
[Extract Text] → [Parse Sections] → [Score Against Requirements]
                                                      ↓
                                               [Extract Skills]
                                                      ↓
                                              [Rank Candidates]
                                                      ↓
                                          [Email Shortlist Report]

Step 1: Receive and Extract Resume Text

Set up a webhook that accepts resume uploads (PDF or DOCX):

// Webhook Node
HTTP Method: POST
Path: /resume-screen
Binary Property: file

// For email-based intake, use IMAP Trigger instead
// and extract attachments from the email

Then extract text from the document. For PDFs, use n8n's built-in PDF extraction or an HTTP Request to a local tool:

// Function Node: Extract Text
// For PDF: use pdf-parse or similar
// For DOCX: extract from XML structure
const text = $input.first().json.text ||
  $input.first().json.body ||
  Buffer.from($input.first().binary.file.data, 'base64').toString('utf-8');

// Clean up extracted text
const cleaned = text
  .replace(/\s+/g, ' ')
  .replace(/[^\x20-\x7E\n]/g, '')
  .trim()
  .substring(0, 4000); // Stay within context window

return [{ json: { resume_text: cleaned } }];

Step 2: Define Job Requirements

Create a structured job requirements template that Ollama will score against:

// Set Node: Job Requirements
{
  "job_title": "Senior Backend Engineer",
  "required_skills": ["Python", "PostgreSQL", "REST APIs", "Docker"],
  "preferred_skills": ["Kubernetes", "AWS", "Redis", "GraphQL"],
  "min_experience_years": 5,
  "education": "BS in Computer Science or equivalent",
  "key_responsibilities": [
    "Design and build scalable microservices",
    "Lead technical design reviews",
    "Mentor junior engineers"
  ]
}

Step 3: AI Scoring with Ollama

The core scoring prompt asks Ollama to evaluate the resume against your requirements:

// HTTP Request to Ollama
{
  "model": "llama3:8b",
  "prompt": "You are an expert technical recruiter. Score this resume against the job requirements.\n\nJob: Senior Backend Engineer\nRequired Skills: Python, PostgreSQL, REST APIs, Docker\nPreferred Skills: Kubernetes, AWS, Redis, GraphQL\nMin Experience: 5 years\n\nResume:\n${resume_text}\n\nRespond in this exact JSON format:\n{\n  \"overall_score\": 0-100,\n  \"skills_match\": {\n    \"required_met\": [\"list of required skills found\"],\n    \"required_missing\": [\"list of required skills NOT found\"],\n    \"preferred_met\": [\"list of preferred skills found\"]\n  },\n  \"experience_years\": estimated_number,\n  \"experience_relevance\": \"high/medium/low\",\n  \"education_match\": true/false,\n  \"strengths\": [\"top 3 strengths for this role\"],\n  \"concerns\": [\"top 3 concerns or gaps\"],\n  \"recommendation\": \"strong_yes/yes/maybe/no\",\n  \"summary\": \"2-3 sentence assessment\"\n}",
  "stream": false,
  "options": {
    "temperature": 0.1,
    "num_predict": 1000
  }
}

Why temperature: 0.1? Resume scoring must be consistent. Two identical resumes should get the same score. Low temperature makes the model deterministic. The structured JSON output format also helps — Ollama follows formatting instructions more reliably at low temperatures.

Step 4: Extract and Structure Results

Parse the AI response and add metadata:

// Function Node: Parse Scoring Results
const response = JSON.parse($json.data).response;
let scoring;

try {
  // Extract JSON from the response
  const jsonMatch = response.match(/\{[\s\S]*\}/);
  scoring = JSON.parse(jsonMatch[0]);
} catch (e) {
  scoring = {
    overall_score: 0,
    recommendation: 'error',
    summary: 'Failed to parse AI response'
  };
}

return [{
  json: {
    candidate_name: $('Extract Text').item.json.candidate_name || 'Unknown',
    email: $('Extract Text').item.json.email || '',
    ...scoring,
    reviewed_at: new Date().toISOString(),
    reviewer: 'AI (Ollama llama3:8b)'
  }
}];

Step 5: Rank and Report

After processing all resumes, sort by score and email the shortlist:

// Function Node: Generate Shortlist Report
const candidates = $input.all()
  .map(item => item.json)
  .sort((a, b) => b.overall_score - a.overall_score);

const shortlist = candidates.filter(c => c.overall_score >= 60);

let report = `# Resume Screening Report\n\n`;
report += `**Position:** Senior Backend Engineer\n`;
report += `**Total Resumes:** ${candidates.length}\n`;
report += `**Shortlisted:** ${shortlist.length}\n\n`;
report += `## Top Candidates\n\n`;

for (const c of shortlist) {
  report += `### ${c.candidate_name} — Score: ${c.overall_score}/100\n`;
  report += `**Recommendation:** ${c.recommendation}\n`;
  report += `**Skills Match:** ${c.skills_match.required_met.join(', ')}\n`;
  report += `**Missing:** ${c.skills_match.required_missing.join(', ') || 'None'}\n`;
  report += `**Summary:** ${c.summary}\n\n`;
}

return [{ json: { report, shortlist_count: shortlist.length, total: candidates.length } }];

Reducing AI Bias in Resume Screening

AI models can inherit biases from training data. Here's how to mitigate this:

  1. Remove identifying information before scoring. Strip names, photos, addresses, and university names from the text before sending to Ollama. Score purely on skills and experience.
  2. Use skills-based scoring only. The prompt above focuses on skills match, experience relevance, and technical fit — not proxies like company prestige or university ranking.
  3. Audit regularly. Run the same diverse set of test resumes monthly to check for scoring drift. Log all scores for compliance.
  4. Human review is mandatory. This is a screening tool, not a hiring tool. Every shortlisted candidate should be reviewed by a human before any outreach.

Scaling for High Volume

For companies receiving 500+ applications per posting:

Complete Workflow JSON

Click to expand full workflow JSON
{
  "name": "AI Resume Screening Pipeline (Ollama)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "resume-screen",
        "responseMode": "lastNode"
      },
      "id": "webhook",
      "name": "Resume Upload Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [240, 300]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "resume",
              "name": "resume_text",
              "value": "={{ $json.body.resume_text || $json.body.text || 'No resume text provided' }}",
              "type": "string"
            },
            {
              "id": "name",
              "name": "candidate_name",
              "value": "={{ $json.body.candidate_name || 'Unknown' }}",
              "type": "string"
            },
            {
              "id": "email",
              "name": "candidate_email",
              "value": "={{ $json.body.email || '' }}",
              "type": "string"
            },
            {
              "id": "job",
              "name": "job_title",
              "value": "={{ $json.body.job_title || 'Senior Backend Engineer' }}",
              "type": "string"
            }
          ]
        }
      },
      "id": "prepare",
      "name": "Prepare Resume Data",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [460, 300]
    },
    {
      "parameters": {
        "url": "http://localhost:11434/api/generate",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: 'llama3:8b', prompt: 'You are an expert technical recruiter. Score this resume against the job requirements.\\n\\nJob: ' + $json.job_title + '\\nRequired Skills: Python, PostgreSQL, REST APIs, Docker\\nPreferred: Kubernetes, AWS, Redis, GraphQL\\nMin Experience: 5 years\\n\\nResume:\\n' + ($json.resume_text || '').substring(0, 4000) + '\\n\\nRespond in JSON: {\"overall_score\": 0-100, \"skills_match\": {\"required_met\": [], \"required_missing\": [], \"preferred_met\": []}, \"experience_years\": N, \"recommendation\": \"strong_yes/yes/maybe/no\", \"summary\": \"2-3 sentences\"}', stream: false, options: { temperature: 0.1, num_predict: 800 } }) }}",
        "options": { "timeout": 120000 }
      },
      "id": "score",
      "name": "Score Resume (Ollama)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [680, 300]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "result",
              "name": "scoring",
              "value": "={{ JSON.parse($json.data).response }}",
              "type": "string"
            },
            {
              "id": "candidate",
              "name": "candidate_name",
              "value": "={{ $('Prepare Resume Data').item.json.candidate_name }}",
              "type": "string"
            },
            {
              "id": "email",
              "name": "candidate_email",
              "value": "={{ $('Prepare Resume Data').item.json.candidate_email }}",
              "type": "string"
            }
          ]
        }
      },
      "id": "output",
      "name": "Format Results",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [900, 300]
    }
  ],
  "connections": {
    "Resume Upload Webhook": {
      "main": [[{ "node": "Prepare Resume Data", "type": "main", "index": 0 }]]
    },
    "Prepare Resume Data": {
      "main": [[{ "node": "Score Resume (Ollama)", "type": "main", "index": 0 }]]
    },
    "Score Resume (Ollama)": {
      "main": [[{ "node": "Format Results", "type": "main", "index": 0 }]]
    }
  },
  "settings": { "executionOrder": "v1" },
  "tags": [{ "name": "AI" }, { "name": "Ollama" }, { "name": "HR" }, { "name": "Recruitment" }]
}

Wrapping Up

AI resume screening with n8n + Ollama gives your recruitment team a fast, private, and customizable first-pass filter. It handles the tedious work of matching skills against requirements and ranking candidates, so recruiters can spend their time on interviews and relationship-building instead of reading 500 PDFs.

The key advantages over cloud-based solutions: your candidate data stays on your infrastructure (critical for GDPR), you pay zero per-resume processing costs, and you have full control over how scoring works — no black-box AI making decisions you can't audit.

Want 11 Production-Ready AI Workflows?

The Self-Hosted AI Workflow Pack includes resume screening, lead scoring, email automation, document processing, and 7 more n8n + Ollama templates. One payment, unlimited runs, zero API costs.

Get the Full Pack — $39