AI CV Screening: Remove 5 Fields Before It Sees a Resume

Abstract tech illustration: AI CV Screening: Remove 5 Fields Before It Sees a Resume

A small team can lose 2 hours a day opening resumes, copying details into a spreadsheet, and trying to decide what deserves a closer look. The tempting shortcut is to upload every CV to an AI tool and let it rank applicants—but that sends names, contact details, addresses, photos, and profile links into a system that should not need them.

A safer approach is simpler: treat AI CV screening as an information-routing workflow, not an automated rejection machine. Remove identifiable fields locally, ask the model for job-specific evidence only, and require a human to approve every next step.

Important: This is an operational and technical blueprint, not legal advice. Employment rules vary by location and role. Review your hiring process with qualified employment counsel, especially before using any automated system in candidate evaluation.

Start with a hard boundary: AI reviews evidence, not identities

The safest AI CV screening workflow keeps applicant identity separate from job evidence from the first file download. The model should receive a random candidate ID, sanitized resume text, and a role rubric—not a name, email address, phone number, home address, photo, or social profile.

This changes the design goal.

The system is not deciding:

  • “Reject this applicant.”
  • “This person is not a fit.”
  • “Rank these people from best to worst.”

It is doing a narrower, auditable job:

  • Extract evidence against defined job requirements.
  • Highlight missing or unclear information.
  • Put the application into a human review lane.
  • Prepare a structured brief so someone does not need to read every CV from scratch.

That boundary matters legally and operationally. The U.S. Equal Employment Opportunity Commission states that federal laws prohibit employment discrimination based on protected characteristics, including race, color, religion, sex, national origin, age, disability, and genetic information. Read the EEOC’s guidance on prohibited employment policies and practices.

A CV often contains proxies for information you should not feed into an automated scoring process:

Field removed before AI processing Why it creates unnecessary risk
Full name May signal ethnicity, gender, or national origin
Email address Often contains a full name or graduation year
Phone number Personally identifiable information with no screening value
Street address Can reveal location, neighborhood, or socioeconomic signals
LinkedIn, portfolio, and profile links Can expose a photo, age clues, personal history, and social data

I also remove images, document metadata, embedded hyperlinks, and filenames. A PDF called Sarah_Jones_Resume_2026.pdf defeats a perfectly sanitized document body if the filename reaches the model or reviewer notification.

The identity-to-candidate-ID mapping should stay in a restricted local database or encrypted file. Only the hiring owner or authorized reviewer needs access to it.

Original resume
    ↓
Local storage: /restricted/hiring/originals/
    ↓
Candidate ID generated: CAND-8F21A9
    ↓
Sanitized text + role rubric
    ↓
AI evidence extraction
    ↓
Anonymous reviewer brief
    ↓
Human approval
    ↓
Authorized reviewer opens original resume if needed

The key rule: no AI output should be able to contact, reject, archive, or advance a candidate on its own.

Pull resumes from a dedicated inbox label with read-only access

A dedicated Gmail label limits what the workflow can see and prevents a screening bot from touching unrelated company email. Give the integration read-only access to a label such as Hiring/New, download only the message ID and attachments, and leave sending, labeling, and moving disabled until a human approves an action.

Do not point an automation at the whole inbox. That is how candidate data, client conversations, invoices, and internal threads end up in the same workflow logs.

For a small business, the inbound boundary can be very narrow:

gmail_ingestion:
  allowed_label: "Hiring/New"
  permissions:
    read_messages: true
    download_attachments: true
    send_messages: false
    modify_labels: false
    delete_messages: false
  accepted_extensions:
    - pdf
    - docx
  max_attachment_count: 3
  candidate_id_format: "CAND-{8_HEX}"

Each received file should get two identifiers:

  1. A random candidate ID used throughout the workflow.
  2. A SHA-256 hash used for duplicate detection and audit records.

The hash is useful when the same applicant submits twice, a recruiter forwards the same CV, or an attachment gets reprocessed after a failed run. It also lets you prove exactly which source document produced a reviewer brief without storing the whole resume in every system.

from hashlib import sha256
from pathlib import Path
from secrets import token_hex

def register_resume(file_path: str) -> dict:
    raw = Path(file_path).read_bytes()

    return {
        "candidate_id": f"CAND-{token_hex(4).upper()}",
        "sha256": sha256(raw).hexdigest(),
        "source_file": Path(file_path).name,
        "bytes": len(raw),
    }

Store this record separately from the AI workflow payload:

{
  "candidate_id": "CAND-8F21A9",
  "sha256": "2c0f0f8d...",
  "original_file_path": "/restricted/hiring/originals/2c0f0f8d.pdf",
  "gmail_message_id": "18f4a92e...",
  "received_at": "2026-08-09T14:22:18Z",
  "status": "pending_sanitization"
}

The model never receives original_file_path, gmail_message_id, sender email, or original filename.

What the intake workflow should stop immediately

A safe intake workflow is allowed to fail closed. Send these files to a manual privacy queue rather than forcing them into AI scoring:

  • Password-protected or corrupted PDFs.
  • Image-only CVs where OCR confidence is low.
  • Unsupported file formats.
  • Documents with no extractable text.
  • Files containing embedded forms, macros, or active content.
  • Duplicate hashes already attached to an active candidate record.

This will create a small manual queue. That is intentional. The alternative is pretending every document can be processed safely and silently producing unreliable output.

Extract text locally, then redact more than five obvious fields

Local extraction should happen before any external AI call, and redaction needs multiple checks—not one regex. Email addresses and phone numbers are straightforward, but names, profile URLs, personal references, document metadata, and scanned text require layered validation.

Use text extraction before OCR. Native PDF text is generally cleaner than OCR output and preserves more reliable page references. Only run OCR when the PDF has no usable text layer.

A practical extraction sequence is:

# Native PDF text extraction
pdftotext -layout resume.pdf resume.txt

# DOCX extraction
pandoc resume.docx -t plain -o resume.txt

# OCR fallback for scanned PDFs
ocrmypdf --skip-text --deskew resume.pdf resume_ocr.pdf
pdftotext -layout resume_ocr.pdf resume.txt

Preserve page markers in the normalized output. When a reviewer sees “Experience with NetSuite invoice exceptions,” they should be able to trace that statement back to page 2, not trust a vague model summary.

import re

def normalize_resume_text(raw_text: str) -> str:
    text = raw_text.replace("\r\n", "\n")
    text = re.sub(r"[ \t]+", " ", text)
    text = re.sub(r"\n{3,}", "\n\n", text)
    return text.strip()

def redact_contacts(text: str) -> tuple[str, dict]:
    report = {
        "emails_found": 0,
        "phones_found": 0,
        "urls_found": 0,
        "flags": []
    }

    email_pattern = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b"
    phone_pattern = r"(?<!\w)(?:\+?1[-.\s]?)?(?:\(?\d{3}\)?[-.\s]?)\d{3}[-.\s]?\d{4}(?!\w)"
    url_pattern = r"(?:https?://|www\.)\S+|(?:linkedin\.com|github\.com|portfolio\.)\S+"

    text, report["emails_found"] = re.subn(email_pattern, "[EMAIL_REMOVED]", text)
    text, report["phones_found"] = re.subn(phone_pattern, "[PHONE_REMOVED]", text)
    text, report["urls_found"] = re.subn(url_pattern, "[PROFILE_LINK_REMOVED]", text)

    return text, report

That code is only the first pass. It does not solve name detection, addresses in unusual formats, OCR mistakes, or contact details hidden in headers and footers.

A better implementation produces a redaction report for every file:

{
  "candidate_id": "CAND-8F21A9",
  "pages_detected": 2,
  "text_extraction_method": "native_pdf",
  "emails_removed": 2,
  "phones_removed": 1,
  "profile_links_removed": 3,
  "images_removed": true,
  "metadata_removed": true,
  "possible_name_tokens_remaining": 1,
  "privacy_status": "manual_review_required"
}

The important field is privacy_status.

If a detector finds an unrecognized email-like string, a probable name in a closing signature, an image-only page, or a residual URL, do not send it to the model. Put it in a human privacy check queue first.

This is where many “AI hiring” demos break down. They show a clean prompt and a score, but they do not build the stop condition between extraction and AI processing.

Put the job rubric in JSON so the model cannot invent the standard

A hiring rubric should be structured data with evidence requirements, not a paragraph asking an AI model whether somebody is “good.” Define required skills, preferred skills, experience thresholds, and permitted clarification questions before screening begins.

The phrase “culture fit” should not exist in the prompt. Neither should school prestige, personality assumptions, age signals, or subjective instructions like “find a hungry self-starter.”

Here is a usable rubric for a customer-facing accounting operations role:

{
  "role_id": "acct-ops-001",
  "role_title": "Accounting Operations Specialist",
  "required_criteria": [
    {
      "id": "accounting_platform",
      "requirement": "At least 2 years using an accounting platform",
      "accepted_evidence": [
        "Named platform such as QuickBooks, Xero, NetSuite, or Sage",
        "Duration tied to accounting platform work"
      ]
    },
    {
      "id": "invoice_exceptions",
      "requirement": "Experience resolving invoice exceptions",
      "accepted_evidence": [
        "Invoice discrepancy handling",
        "Vendor payment issues",
        "Purchase order matching",
        "Accounts payable exception workflows"
      ]
    },
    {
      "id": "written_support",
      "requirement": "Written customer or vendor support experience",
      "accepted_evidence": [
        "Email support",
        "Customer communication",
        "Vendor communication",
        "Ticketing system work"
      ]
    }
  ],
  "preferred_criteria": [
    {
      "id": "spreadsheet_reporting",
      "requirement": "Spreadsheet reporting or reconciliation experience"
    }
  ],
  "human_confirmation_only": [
    "Work authorization, where legally appropriate",
    "Availability to work required business hours",
    "Compensation expectations"
  ]
}

The model’s job is evidence extraction, not judgment. Ask it to return one of three lanes:

Review lane Meaning Human action
review_first Strong, specific evidence against most required criteria Review first, then decide
standard_review Some relevant evidence, but no clear priority Review in normal order
needs_clarification Material requirement is unclear or evidence is missing Ask a human-approved follow-up question

There is no automatic reject lane.

The prompt should explicitly prevent common failure modes:

You are an evidence extraction assistant for a human hiring reviewer.

You must not make an employment decision.
You must not recommend rejection, hiring, or ranking people.
You must not infer age, gender, race, ethnicity, disability, religion,
nationality, family status, personality, or other protected characteristics.

For every rubric criterion:
1. Return direct evidence from the sanitized resume.
2. Include the page reference.
3. Say "no evidence found" when the document does not support the criterion.
4. Do not treat missing information as a negative fact.
5. Return valid JSON only.

That instruction is not legal protection by itself. It is a technical control that makes the workflow easier to inspect, test, and constrain.

Validate AI output in code and route uncertainty to people

Model output must be validated before it reaches a reviewer queue. If the JSON is malformed, a quote cannot be found in the sanitized text, a required field is missing, or confidence is low, the workflow should route the CV to review_first with a processing flag.

Do not trust a polished-looking score. A model can produce valid prose with unsupported claims, misplaced page references, or incorrect assumptions about a job title.

Use a fixed schema. For example:

{
  "candidate_id": "CAND-8F21A9",
  "review_lane": "standard_review",
  "criteria": [
    {
      "criterion_id": "accounting_platform",
      "status": "evidence_found",
      "evidence_quote": "Used QuickBooks Online to reconcile vendor invoices...",
      "page": 1
    },
    {
      "criterion_id": "invoice_exceptions",
      "status": "evidence_found",
      "evidence_quote": "Resolved purchase order and invoice discrepancies...",
      "page": 2
    },
    {
      "criterion_id": "written_support",
      "status": "no_evidence_found",
      "evidence_quote": null,
      "page": null
    }
  ],
  "missing_or_ambiguous_evidence": [
    "No direct evidence of written customer or vendor support."
  ],
  "questions_for_reviewer": [
    "Should written support experience be confirmed in a follow-up?"
  ],
  "confidence": "medium"
}

Then verify that the quotes exist in the sanitized source text:

def validate_evidence(ai_result: dict, sanitized_text: str) -> list[str]:
    errors = []

    allowed_lanes = {"review_first", "standard_review", "needs_clarification"}
    if ai_result.get("review_lane") not in allowed_lanes:
        errors.append("Invalid review lane")

    for item in ai_result.get("criteria", []):
        quote = item.get("evidence_quote")
        status = item.get("status")

        if status == "evidence_found" and not quote:
            errors.append(f"Missing quote for {item.get('criterion_id')}")

        if quote and quote.lower() not in sanitized_text.lower():
            errors.append(
                f"Ungrounded evidence quote for {item.get('criterion_id')}"
            )

    return errors

A production workflow should log the validation result, prompt version, rubric version, model version, and timestamp. This gives you a traceable record when a reviewer asks, “Why did this application appear in my queue?”

The reviewer brief should be short and anonymous

The final notification should contain only what the reviewer needs to prioritize reading:

Candidate: CAND-8F21A9
Role: Accounting Operations Specialist
Lane: Standard review
Confidence: Medium

Evidence found:
- QuickBooks Online and reconciliation work — page 1
- Invoice and purchase order discrepancy handling — page 2

Unclear:
- No direct evidence of written customer or vendor support

Reviewer action:
- Read original CV
- Decide whether to request clarification
- Approve or decline the next step

Send that to a secure Gmail review label, internal dashboard, or private Telegram workflow. Do not put names, email addresses, original attachments, or profile links in the notification.

The reviewer can open the original file through an authenticated local link only after deciding that they need identifying information. That preserves the separation between evidence review and identity access.

Why bizflowai.io helps with this

bizflowai.io builds approval-gated hiring workflows for small teams that receive applications through Gmail but do not need a full ATS. The systems can collect attachments from a dedicated label, create candidate IDs and file hashes, extract and sanitize CV text locally, apply role-specific evidence rubrics, validate structured AI output, and send anonymous reviewer briefs without allowing the automation to reject candidates or contact them on its own.


Want more like this?

I publish practical AI automation, GenAI engineering, and faceless content workflows on YouTube every week.

Subscribe to bizflowai.io on YouTube — never miss a new tutorial.

Planning an AI automation project or need a second opinion on your architecture?

Connect with me on LinkedIn — Lazar Milicevic, GenAI Engineer & bizflowai.io Founder.

Visit bizflowai.io for our services, case studies, and AI consulting.

Frequently asked questions

What is a safer AI CV screening workflow?

A safer AI CV screening workflow routes candidate information for human review rather than automatically rejecting applicants. It uses read-only access to a dedicated hiring email label, assigns random candidate IDs, stores original files in a restricted folder, removes identifying information before AI processing, and requires the model to return evidence tied to a job rubric. Candidates with privacy or extraction issues are sent to manual review.

How do I anonymize CVs before sending them to an AI model?

Extract text from PDF or DOCX files, using OCR only for scanned PDFs. Normalize the text while preserving page markers, then replace names, email addresses, phone numbers, street addresses, and social-profile links with tokens. Remove document metadata, images, and embedded hyperlinks as well. Create a redaction report and route files with leftover contact details, image-only content, or low-confidence extraction to a manual privacy check.

Why does evidence-based CV screening matter for hiring?

Evidence-based CV screening makes AI output easier for hiring managers to inspect and verify. Instead of asking whether a candidate is good, the workflow asks the model to return evidence for each job criterion or state that no evidence was found. The model includes direct quotes, page references, missing or ambiguous evidence, unanswered questions, confidence, and a review lane, helping humans make the final decision.

How do I create a job rubric for AI CV screening?

Create a small JSON rubric for each role rather than relying on a vague prompt. Include required skills, preferred skills, minimum relevant experience, legally permitted work-authorisation questions, and knockout questions that require human confirmation. Define the evidence needed for every criterion. Avoid culture fit, personality, age signals, school prestige, and protected characteristics, because the system should identify documented evidence rather than infer personal qualities.

When should I use manual review instead of AI CV scoring?

Use manual review when privacy redaction or document extraction is uncertain. A CV should not enter AI scoring if the workflow finds an unrecognised email-like string, suspicious leftover contact details, an image-only PDF, or low-confidence extraction. Manual review is also needed for knockout questions requiring human confirmation. A safe screening process must be able to stop rather than automatically process every application.