340 'Just Checking In' Emails: The Recruiter Inbox Agent

Abstract tech illustration: 340 'Just Checking In' Emails: The Recruiter Inbox Agent

Post a role, get 100 applicants in 72 hours, and by month's end your recruiter has 340 messages in Gmail — 89% of them asking the same question: what's the status of my application? Average 47 seconds each. That's 4.4 hours a week on copy-paste that requires zero judgment. Every AI recruiting tool automates sourcing, screening and scheduling. None of them touch the inbox flood. Here's the exact n8n agent that does.

Why the ATS vendors don't fix this

The inbox is the bottleneck the tooling refuses to look at. ATS platforms watch their own portal. Candidates don't use the portal — they reply to the human they talked to, which is the recruiter's Gmail. So the traffic that actually matters lands in an unstructured freeform channel that no vendor's automation touches.

Here's what the volume looks like in the deployments I've measured for small-team hiring (1-3 open roles at a time):

Metric Per role Per month (3 roles)
Applicants ~100 ~300
Follow-up emails ~340 ~1,020
Status-check share 89% 89%
Recruiter seconds/email 47 47
Weekly time cost 4.4 hrs ~13 hrs

Thirteen hours a week of a recruiter's calendar burned on "you're still under review." That's not a workflow problem, it's a missing integration. The fix is five n8n nodes and about two hours to wire up.

Node 1 & 2: Gmail trigger + a tight intent classifier

Direct answer: A Gmail trigger filtered to candidate mail feeds a single LLM classification call that returns one of three labels (status_check, new_question, withdraw) plus a confidence score. Anything under 0.8 confidence is routed to a human. No creativity allowed at this node — three labels, one number, done.

The intake layer is a Gmail trigger pointed at the recruiter's inbox, filtered by a label. If you don't already tag inbound candidate mail, add a Gmail filter that matches the job posting URL, ATS ID, or the "reply to" pattern from your outbound sequences. Clean intake matters because everything downstream trusts it.

The classifier prompt is short on purpose:

You classify inbound recruiting emails into exactly one of:
- status_check: candidate asking where they stand
- new_question: candidate asking about salary, benefits,
  remote, timeline, or anything requiring a real answer
- withdraw: candidate is pulling their application

Return ONLY valid JSON:
{"intent": "<label>", "confidence": <0.0-1.0>}

Email subject: {{ $json.subject }}
Email body: {{ $json.body }}

Model choice: any small-context model works here. GPT-4o-mini or Claude Haiku both classify these three intents at ~98% accuracy on my test set of 42 seeded emails. Cost is roughly $0.0004 per email at current pricing — call it $0.13/month for 340 emails. Not a line-item worth optimizing.

The 0.8 confidence gate exists because the two intents most likely to blur are status_check and new_question — a candidate asking "any update on timing? also is this remote?" is genuinely both. Let those get a human eye rather than a wrong autoreply.

Node 3: The status lookup (the node every builder skips)

Direct answer: Before the LLM writes a single word of reply, you look up the candidate's actual pipeline stage from your source of truth — Google Sheets, Airtable, or your ATS API — and pass that string into the reply node. Facts get looked up. Language gets generated. Never mix the two.

This is where trust lives or dies. If you let the model guess pipeline stage, it will hallucinate one, the candidate will call you out on Twitter, and your agent is done. I've watched it happen. The fix is a dumb key-value lookup that runs before generation:

// n8n Function node: normalize sender email
const email = $input.item.json.from
  .match(/<([^>]+)>/)?.[1]
  ?.toLowerCase()
  ?? $input.item.json.from.toLowerCase();

return { email };

Then a Google Sheets (or Airtable) node with a lookup by that normalized email. The output is a plain string from a fixed vocabulary:

  • applied
  • phone_screen_scheduled
  • phone_screen_done
  • technical_round
  • onsite_scheduled
  • offer_pending
  • rejected
  • no_match (email not found in ATS)

no_match is important. If the sender isn't in your pipeline — recruiter spam, a candidate who applied via a partner referral that never made it into the sheet, a mis-labeled email — the agent must not guess. It goes to the human queue.

One more thing: normalize both sides. Gmail addresses are case-insensitive but your ATS entries might not be. Lowercase both. Strip the +tag suffix if you want to be safe. I've seen lookups miss 6% of candidates because someone entered them as Firstname.Lastname@... and Gmail delivered firstname.lastname@....

Node 4: Template replies with LLM personalization (not the other way around)

Direct answer: Do not let the LLM write the whole email. Use three hard-coded template branches — early stage, mid stage, final stage — with locked factual language about next steps and timelines. The LLM personalizes only the greeting and closing sentence. That's what keeps facts bulletproof and tone human.

Every builder I've seen wire this up wants to let the model draft freely. Within a week they've told three candidates they got an offer when they didn't. Don't.

The template map:

Pipeline stage Template branch Fixed content
applied Early "Your application is with the hiring manager. First-round decisions go out within 7 business days."
phone_screen_scheduled, phone_screen_done Early "You're in the phone screen stage. We'll be in touch within 5 business days of your last conversation."
technical_round, onsite_scheduled Mid "You're in the interview loop. Final feedback typically comes 3-5 business days after your last session."
offer_pending Final "Your file is with the leadership team for a final decision. You'll hear back this week."
rejected (never auto-reply) Route to human. Rejection follow-ups need judgment.

The LLM sees the candidate's first name, the raw email body (for tone matching), and the fixed template. It returns two fields:

{
  "greeting": "Hi Priya,",
  "closing": "Appreciate your patience through the process — we know these wait windows are hard."
}

Everything between greeting and closing is stitched in as literal template text. The candidate gets a warm, personalized-feeling email where every factual claim came from your sheet, not a language model.

Rejection is deliberately excluded from auto-reply. Even with a lookup, telling someone "no" over an autoresponder is where trust breaks. Route those to the recruiter's draft folder.

Node 5: The human handoff (11% of volume)

Direct answer: If intent is new_question, or classifier confidence is under 0.8, or the pipeline lookup returned no_match, the agent drafts the reply into Gmail as a draft, pings the recruiter on Slack or Telegram with the candidate name and the flagged reason, and stops. It never sends without approval on this branch.

In the deployments I've measured, this covers about 11% of inbound volume:

  • ~6% are legitimate new questions (comp, remote, visa, start date)
  • ~3% are ambiguous / low confidence
  • ~2% are no_match lookups

The Slack ping is minimal:

Candidate: Priya Kadam <priya.k@gmail.com>
Flag: new_question
Excerpt: "Quick one — is the role open to sponsorship?"
Draft: https://mail.google.com/mail/u/0/#drafts/...

Even on that 11%, you've saved read time and the initial write. Per-email cost drops from 47 seconds to about 12. The recruiter opens the draft, edits or approves, sends. On the 89% auto-handled, cost drops to zero recruiter time.

Full math on a 340-email month:

Manual With agent
302 auto-replies × 47s 3.94 hrs 0
38 human-review × 47s → 12s 0.50 hrs 0.13 hrs
Total 4.4 hrs 0.13 hrs

That's 4.27 hours a week back, or roughly 18 hours a month per active recruiter. At a $65k recruiter fully-loaded, that's ~$620/month of recovered capacity.

What the test run actually showed

I seeded a test inbox with 42 follow-up emails spread across the three intents — 36 status checks, 4 new questions, 2 withdrawals. Cron set to every 5 minutes.

  • 42/42 classified correctly on intent
  • 2 status-checks came back at 0.74 confidence and correctly routed to the human queue
  • 40 auto-handled; 36 status replies, all with the correct pipeline stage pulled live
  • 0 hallucinated stages
  • Median end-to-end latency: 4.2 seconds from Gmail delivery to reply sent
  • Total LLM cost for the batch: $0.03

The two false-negative-on-confidence cases were both emails that mixed a status question with a scheduling question ("any update? also I can't do Thursday"). Exactly the kind of thing you want a human seeing.

Why bizflowai.io helps with this

I build recruiter inbox agents like this for small hiring teams as part of the standard automation stack at bizflowai.io — the intake filter, the classifier prompt, the source-of-truth lookup against whatever ATS or Sheet you're already using, and the Slack/Telegram handoff. Most clients are running it inside a week and stop thinking about "just checking in" emails entirely. If you're a small team where the recruiter is also the hiring manager is also the founder, this is usually the first node worth wiring up.


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 the recruiter inbox bottleneck that AI sourcing tools miss?

The recruiter inbox bottleneck is the flood of freeform status-check emails candidates send directly to recruiters instead of using an ATS portal. Roughly 89% of these messages ask the same question: what's my application status. Existing recruiting AI tools automate sourcing, screening, and scheduling inside structured workflows, but none watch Gmail for inbound candidate messages, so the bottleneck sits exactly where the tooling doesn't look.

How do I automate candidate status-check replies in n8n?

Build a five-node n8n workflow. First, a Gmail trigger filtered by label or job posting reference. Second, an LLM intent classifier returning one of three labels (status check, new question, withdraw) with a confidence score. Third, a status lookup node that queries your ATS or Google Sheet by email. Fourth, a template-based reply generator. Fifth, a human handoff for low-confidence cases. It takes about two hours to build.

Why does looking up pipeline status before generating the reply matter?

You must look up the candidate's pipeline stage from your source of truth before the LLM writes anything. If you let the model guess the stage, it will hallucinate, the candidate will catch the error, and trust in the system collapses. The rule is simple: facts get looked up, language gets generated, and the two are never mixed. This is the node most builders skip.

When should the LLM write the full email versus use templates?

The LLM should never write the full email in a recruiting status reply. Use three hard-coded template branches for early, mid, and final pipeline stages, with locked factual language about next steps and timing. Let the LLM personalize only the greeting (candidate name) and one closing sentence of warmth. Letting the model write freely has caused clients to falsely tell candidates they got offers within a week.

How much time do recruiters waste on manual status-check emails?

A recruiter handling 100 applicants can receive around 340 follow-up messages a month, with 89% asking for application status. Each reply averages 47 seconds: open the email, alt-tab to the ATS, find the candidate, check the stage, type a reply, send. That adds up to 4.4 hours per week, more than half a workday, spent on copy-paste work that requires zero judgment.