WhatsApp Hiring Agent: 92 Applicants, 4 Days, $0.47

The owner's wife was copy-pasting phone numbers into a spreadsheet until midnight, typing the same three screening questions into WhatsApp threads over and over. Miss one evening, candidates ghost. Don't miss one, you don't have evenings. This is what recruiting looks like for a 40-person service business hiring drivers, and no ATS on the market is built for it.
Why email-first recruiting tools fail blue-collar hiring
For service, trades, delivery and retail roles, the candidate never opens Gmail. Roughly 70% apply from a phone by tapping the WhatsApp link in the job ad, and if you don't reply inside two hours they're already talking to your competitor. Every recruiting tool I've evaluated — Greenhouse, monday.com's recruiting template, Workable, the whole category — assumes a knowledge-worker candidate with a polished CV who checks email six times a day.
That candidate doesn't exist for a regional service business getting 90 to 150 applications a week for a driver role. The funnel starts on WhatsApp. If your pipeline starts in email, you lose the applicant before screening.
- Email-first tools bury the two-hour response window under form-fill flows
- ATS "career page" links convert badly from mobile job-board ads
- No native way to score a candidate whose entire application is three chat replies
The fix isn't a better ATS. It's an agent that lives where the candidate already is.
The stack: n8n, Postgres, WhatsApp Business API, GPT-4o-mini
The whole system runs on a $6/month VPS with n8n, Postgres for candidate state, the official WhatsApp Business Cloud API for messaging, and GPT-4o-mini for scoring. No ATS, no Greenhouse seat, no monday.com license. Total moving parts: four.
Here's why each choice:
- n8n — visual workflow, easy for the client to open and understand. Self-hosted on Hetzner for $6/month. Handles the webhook, branching, and cron.
- Postgres — one
candidatestable plus oneconversationsJSON column. That's the entire schema. No ORM. - WhatsApp Business API (Cloud, Meta-hosted) — free tier covers small volume, pennies per conversation after. Service conversations initiated by the user are the cheap ones, which is exactly this flow.
- GPT-4o-mini — $0.15 per 1M input tokens, $0.60 per 1M output. Screening a candidate with three short answers plus a job-spec prompt costs about half a cent.
The candidate row looks like this:
CREATE TABLE candidates (
id SERIAL PRIMARY KEY,
phone TEXT UNIQUE NOT NULL,
full_name TEXT,
status TEXT NOT NULL DEFAULT 'new',
score INT,
score_reason TEXT,
conversation JSONB DEFAULT '[]'::jsonb,
job_id TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
last_msg_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX ON candidates (status);
CREATE INDEX ON candidates (last_msg_at);
Status values in production: new, intake, screening, interview-scheduled, rejected, abandoned, hired. Seven states, one column, easy to filter.
Step 1-2: Entry point and structured intake
The job ad's WhatsApp click-to-chat link uses https://wa.me/<number>?text=hi%2C%20I%27d%20like%20to%20apply%20for%20the%20driver%20position. That prefilled message hits Meta's webhook, which posts to an n8n Webhook node. The first thing the workflow does is a Postgres lookup on the phone number.
New number → insert row with status='intake' and fire the three intake messages. Existing number → route to a conversation handler that reads status and continues from the right step.
// n8n Function node — routing logic
const phone = $json.entry[0].changes[0].value.messages[0].from;
const text = $json.entry[0].changes[0].value.messages[0].text.body;
const existing = await pg.query(
'SELECT id, status FROM candidates WHERE phone = $1', [phone]
);
if (existing.rows.length === 0) {
return { route: 'intake_start', phone, text };
}
return { route: existing.rows[0].status, phone, text, id: existing.rows[0].id };
The intake asks three questions, one at a time, with a 4-8 second delay between each so it doesn't feel like a form:
- What's your full name?
- Do you have a valid driver's license (class B or higher)?
- When can you start?
Each reply comes back through the same webhook and gets appended to the conversation JSONB column. This is the part most builders overbuild. You do not need LangChain, a memory buffer, or a "conversational agent" here. You need three structured questions and a timeout.
The timeout is a cron workflow that runs every hour:
- If
status='intake'andlast_msg_atis between 24h and 72h old → send one nudge message - If
status='intake'andlast_msg_atis older than 72h → flip toabandoned
That's it. No re-engagement drip. Blue-collar candidates who ghost for three days are gone.
Step 3-4: Scoring with GPT-4o-mini and the branch
Once all three intake answers are in, n8n calls GPT-4o-mini with the job spec as a JSON block, the candidate's answers, and a strict output schema. The model returns a score 0-10 and a one-sentence reason. That JSON writes straight back to Postgres. No free text, no vibes.
The prompt is short on purpose — every extra token is money at scale:
SYSTEM = """You screen candidates for a service business.
Return ONLY valid JSON: {"score": int 0-10, "reason": "one sentence"}.
Score based on how well the candidate matches the job spec.
Any missing hard requirement = score below 5."""
USER = f"""JOB_SPEC:
{{
"role": "driver",
"required_license": "B",
"min_age": 21,
"start_within_days": 14,
"language": "English"
}}
CANDIDATE_ANSWERS:
- Full name: {name}
- License: {license_answer}
- Start date: {start_answer}
"""
A real response from the log:
{"score": 7, "reason": "Has license and availability but starts in three weeks."}
Then an IF node branches on the score:
- Score ≥ 7 → send a WhatsApp message with a Cal.com booking link for a 15-minute phone interview, set
status='interview-scheduled' - Score < 7 → send a polite rejection template, set
status='rejected'
The Cal.com link is what kills the midnight-WhatsApp problem. Candidates pick their own slot. Cal.com fires a webhook back into n8n on booking, which updates the row and sends a confirmation. The owner sees a filled calendar the next morning.
One implementation detail worth calling out: WhatsApp requires pre-approved templates for business-initiated messages, but replies inside a 24-hour service window can be free-form. Because every message in this flow is a reply to something the candidate sent, we stay in the service window and don't need template approval for rejection or booking-link messages. Read the WhatsApp Cloud API pricing docs before you build — the conversation-based pricing model matters for cost math.
Step 5: A dashboard the client will actually open
The client doesn't want to open n8n. They want one screen that says who to call today. So the "dashboard" is a Postgres view piped into a Retool page. Five columns: name, phone, score, status, last message time. Filter by status='interview-scheduled', sort by last_msg_at descending, done.
CREATE VIEW v_recruiter_today AS
SELECT full_name, phone, score, status, last_msg_at
FROM candidates
WHERE status IN ('interview-scheduled', 'intake', 'screening')
ORDER BY last_msg_at DESC;
The owner opens Retool on their phone, sees the interviews booked for the day, calls them. That's the whole UX. No Kanban, no drag-and-drop pipeline, no notification center.
The receipts: 4 days, 92 candidates, $0.47
I don't estimate numbers. Here's what the Postgres table showed after the first four days live:
| Metric | Count |
|---|---|
| Total candidates ingested | 92 |
| Completed intake | 61 |
| Rejected by score | 31 |
| Interview scheduled | 14 |
| Abandoned (72h no reply) | 24 |
| Still in intake window | 23 |
OpenAI dashboard for the same window: $0.47 total spend. That's roughly half a cent per candidate for the full screening pass. VPS is $6/month. WhatsApp Business API conversations for this volume ran under $3 for the four days. Everything in for the pilot week: under $10.
The client filled two driver positions that week from candidates the agent scheduled autonomously. The owner's wife got her evenings back. The agent kept running while everyone slept.
The pattern isn't specific to hiring. Same architecture — WhatsApp webhook → Postgres state → LLM scoring → branch → booking link — works for lead intake, support triage, appointment booking, any workflow where your customer or candidate lives on their phone and your current tool assumes they live in a browser tab.
Where bizflowai.io fits
This build is exactly the kind of thing I ship for clients through bizflowai.io — small-team automation where the "official" SaaS category (ATS, CRM, help desk) is priced and designed for a company 100x bigger than the buyer. When the funnel is chat-first, the fix is usually not a subscription. It's a small n8n workflow, a Postgres table, an LLM call scoped tightly enough to cost cents, and a Retool page the owner will actually open on their phone. Most of the client work I do collapses a $400/month SaaS bill into a $6 VPS plus a weekend of build time.
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 WhatsApp-first recruiting pipeline for blue-collar hiring?
It's an automated hiring funnel where candidates apply via a WhatsApp click-to-chat link instead of email. A webhook triggers an n8n workflow that stores the candidate in Postgres, sends three screening questions, scores answers with GPT-4o-mini, and books qualified candidates into a Cal.com interview slot. It suits service and field roles where 70% of applicants apply from their phone and expect replies within two hours.
How do I build an automated candidate screening workflow with n8n and GPT-4o-mini?
Run n8n on a cheap VPS with Postgres for state and the WhatsApp Business API for messaging. When a candidate replies, append answers to a conversation JSON field. Once three intake answers are collected, call GPT-4o-mini with the job spec and answers, asking for a 0-10 score plus a one-sentence reason returned as structured JSON. An IF node then routes candidates to booking or rejection.
Why does WhatsApp matter for hiring blue-collar and service workers?
Around 70% of blue-collar applicants apply from their phone by tapping a WhatsApp link in the job ad. If you don't reply within two hours, they've already moved on to a competitor. Email-first pipelines lose these candidates before screening even begins because service workers don't check email frequently or maintain polished CVs, unlike the knowledge workers most recruiting tools are designed for.
When should I use Cal.com self-booking instead of manual interview scheduling?
Use Cal.com self-booking whenever candidate volume is high enough that manual back-and-forth becomes a bottleneck, such as 90-150 applicants per week. Candidates pick their own interview slot via a WhatsApp link, Cal.com fires a webhook to update Postgres, and a confirmation is sent automatically. This eliminates evening scheduling work and prevents ghosting caused by delayed replies from a single human coordinator.
How do I prevent candidates from ghosting during automated screening?
Send three intake questions back-to-back with short human-feeling delays right after the candidate messages you. If they don't reply within 24 hours, a cron workflow sends one nudge. If they still don't reply within 72 hours, flip their status to abandoned and stop messaging. Fast initial response plus a single timed nudge captures most candidates without spamming those who've genuinely lost interest.