# 218 of 312 Applicants Rejected Before a Human Looked

> Source: <https://dev.to/lamingsrb/218-of-312-applicants-rejected-before-a-human-looked-4pig>
> Published: 2026-08-10 06:12:26+00:00

Every recruiting AI demo shows the same three things: source, screen, schedule. Nobody shows you the tier that actually saves the hours — the guilt-driven manual review of obvious no's. Here's the exact n8n flow that killed 218 of 312 applications on one role before a recruiter wasted a minute.

One role. Mid-level ops position. 312 applications in 11 days. The recruiter I built this for was spending roughly nine hours per week opening CVs, reading the first paragraph, and closing them again because the candidate was in the wrong country, wanted double the salary band, or didn't hold the license the role legally requires. Zero hires from that pile.

That's the actual leak. And it's the one nobody automates, because every founder-influencer is busy building outreach bots for the top of the funnel where it looks impressive on a demo.

We flipped it. Automated the rejection tier first. Here are the real numbers after 11 days on one role:

| Metric | Before | After |
|---|---|---|
| Applications | 312 | 312 |
| Auto-rejected with reason code | 0 | 218 |
| Promoted to human review | 312 | 94 |
| Recruiter time | ~9 hrs/week | ~90 min/week |
| Token cost (Claude Haiku) | $0 | $0.41 |
| False negatives (month 1) | n/a | 2 out of ~600 |

That's the whole pitch. Now the build.

This is where most teams ruin the pipeline before they start. Do not build a scoring model. Start with hard-fail criteria — binary gates that a person could defend in writing to a candidate, a lawyer, or a labor board.

For this ops role, we encoded six:

No culture fit. No "communication skills." No vibes. Soft signals belong later, after a human is already in the loop.

The reason this matters is legal and operational. Soft scoring produces disputes. Hard criteria produce receipts. When a candidate emails back asking why they were rejected, the recruiter points at one line: "You indicated Berlin as your base. The role requires on-site presence in Lisbon." That reply writes itself.

In the US, the EEOC's [guidance on AI in employment decisions](https://www.eeoc.gov/ai) is clear that automated tools can create disparate impact liability under Title VII. Binary, role-relevant criteria with evidence snippets are defensible. Opaque 1-10 scores are not.

Every application lands in a shared Gmail inbox. The ATS forwards there. A Gmail label — `new-application`

— fires the n8n workflow.

The flow is boring on purpose:

```
Gmail Trigger (label: new-application)
  → Extract email body + sender
  → Download attachment (PDF/DOCX)
  → PDF-to-text node (pdf-parse)
  → Merge: {email_body, cv_text, applicant_id}
  → HTTP Request → Anthropic Messages API (claude-haiku)
  → Parse JSON response
  → IF any_fail == true → Airtable: reject_queue
  → ELSE → Airtable: human_review + Gmail label: needs-review
```

Model choice matters. I use Claude Haiku, not Sonnet, not Opus. We're not writing poetry — we're checking six checkboxes. Haiku runs at roughly $0.25 per million input tokens and $1.25 per million output tokens (check Anthropic's [current pricing](https://www.anthropic.com/pricing) before you scale). A typical CV + cover email + prompt clocks in around 3,500 input tokens and 400 output tokens per call. That's how 312 applications came in at 41 cents total.

Sonnet would have cost roughly 12x more with zero accuracy gain on binary extraction. Save Sonnet for the tasks where reasoning actually matters.

Do not ask the model for a holistic score from 1 to 10. That's the single biggest failure mode in every hiring AI I've audited. Ask criterion by criterion, demand a quoted evidence snippet, and force a strict JSON schema.

Here's the shape of the prompt:

```
SYSTEM = """You evaluate a candidate against exactly six binary criteria.
For each criterion return:
  verdict: "pass" | "fail" | "unclear"
  evidence: exact quoted sentence from the CV or cover email
            that led to your decision, or null if none exists
  confidence: "high" | "low"

Do not infer. Do not guess. If the CV does not explicitly state
the fact needed to evaluate a criterion, return "unclear".
Return only valid JSON matching the provided schema."""

USER_TEMPLATE = """
CRITERIA:
1. work_authorization: Candidate has legal right to work in {country}.
2. location: Candidate is based within {radius_km} km of {city}.
3. experience: Candidate has at least {min_years} years in {domain}.
4. salary: Candidate's stated expectation is within {band_low}-{band_high} {currency}.
5. license: Candidate holds a valid {license_name}.
6. language: Candidate is fluent (B2 or higher) in {language}.

CV TEXT:
{cv_text}

COVER EMAIL:
{email_body}

Return JSON: {{"criteria": [{{"name": "...", "verdict": "...", "evidence": "...", "confidence": "..."}}]}}
"""
```

The routing logic that follows is trivial:

``` js
// n8n Function node
const results = JSON.parse($json.claude_response).criteria;
const hard_fail = results.some(c => c.verdict === "fail" && c.confidence === "high");
const has_unclear = results.some(c => c.verdict === "unclear");

return {
  route: hard_fail ? "reject_queue" : "human_review",
  triggered_by: hard_fail
    ? results.find(c => c.verdict === "fail" && c.confidence === "high").name
    : null,
  results
};
```

Notice what's not there. No "score > 7 means promote." No weighting. If any single hard criterion fails with high confidence, the candidate goes to the silent-reject queue. Unclear or all-pass goes to a human. That's the whole decision tree.

This is what turns the flow from a black box into a tool a non-technical recruiter can actually trust and tune.

Every decision — pass or fail — writes a row to Airtable. Three columns matter most:

| Column | Example |
|---|---|
`applicant_id` |
`2026-ops-0184` |
`triggered_criterion` |
`location` |
`evidence_snippet` |
"Currently based in Berlin, open to remote roles across EU." |
`verdict_full_json` |
`{...}` (full 6-criteria response) |
`route` |
`reject_queue` |
`timestamp` |
`2026-08-05T14:22:11Z` |
`model_version` |
`claude-haiku-2026-xx` |

Once a week, the recruiter opens Airtable, sorts by `triggered_criterion`

, and asks two questions:

If the answer is no, they don't touch the prompt. They adjust the criteria list for that role — the role config, in Airtable, in plain English. The prompt stays generic. Criteria are config. That separation is what lets a non-technical recruiter tune this weekly without calling me.

Three times a week, the workflow randomly pulls one CV from the reject pile and forwards it to the recruiter anyway, flagged as a shadow review. The recruiter reads it in 90 seconds and either confirms the rejection or flags a false negative.

``` js
// n8n Cron: Mon/Wed/Fri 09:00
const rejects = await airtable.list("reject_queue", {
  filterByFormula: "AND(IS_AFTER(timestamp, DATEADD(NOW(), -2, 'days')), NOT({shadow_reviewed}))"
});
const sample = rejects[Math.floor(Math.random() * rejects.length)];
await gmail.send({
  to: recruiter,
  subject: `[SHADOW REVIEW] ${sample.applicant_id} — rejected on ${sample.triggered_criterion}`,
  body: buildShadowReviewEmail(sample)
});
```

Over the first month we caught two false negatives out of roughly 600 rejections. Both were candidates whose CVs were poorly formatted — one was a PDF that was actually an image scan, the other used a two-column layout that scrambled during text extraction. The model wasn't wrong. The input was garbage.

We fixed the extraction step (added an OCR fallback for image PDFs, added a layout-aware parser for multi-column), not the model. If you don't have a shadow-review layer, you'll drift and you won't know you're drifting until a great candidate posts a screenshot on LinkedIn.

Hard rule. The bot does not send the rejection email. Ever.

Several jurisdictions have disclosure requirements for automated hiring decisions. New York City's Local Law 144 requires bias audits and candidate notification for automated employment decision tools. Illinois has the AI Video Interview Act. The EU AI Act classifies most hiring AI as high-risk. The last thing a small business needs is a discrimination complaint over an email nobody read before it went out.

What we do instead: the silent-reject queue sits in Airtable. Once a week, the recruiter reviews it in bulk, spot-checks the evidence snippets, and then sends rejection emails from their own inbox using a templated response that quotes the specific criterion. A human clicks send. That's the compliance line, and it's cheap to hold.

We build these silent-reject pipelines for recruiter clients as a standard workflow — the Gmail trigger, the Claude Haiku evaluator, the Airtable audit log, the shadow-review sampler, and the role-config sheet that a non-technical recruiter can edit weekly. Most clients go live in under a week and see their manual CV-reading time drop by 70-85% on the first role they migrate. The pattern is the same across ops, sales, and support roles — only the six criteria change.

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](https://bizflowai.io) for our services, case studies, and AI consulting.
