Field SOP
Field SOP

Resume Screening Workflow: Score by JD and Route in One Pass

Receiving two to three hundred resumes per JD is slow and error-prone by hand. This n8n workflow funnels resume screening into one pipe: Webhook/email intake -> extract text -> LLM scores skill match/experience/stability/risk against the JD and outputs a tier plus reason -> IF checks whether it clears the bar -> Switch routes to interview queue/talent pool/rejection email -> write to Feishu Bitable or ATS -> notify recruiter. The Prompt explicitly forbids using gender/age/marital status/race/household registration as factors; resume handling must comply with PIPL/GDPR; AI scoring is assistive only and the final call belongs to a human.

Published August 4, 20265 min read
<!-- workflow-resume-screening | resource | Resume Screening Workflow: Score by JD and Route in One Pass -->

Receiving two or three hundred resumes for one job posting is normal during hiring season. HR opens each PDF, eyeballs it against the JD, scores it in Excel, then schedules interviews or sends rejection letters-five minutes per resume on average, twenty-five hours of pure manual work for three hundred, and by the time the round closes half a month is gone. Worse, scoring drifts: the first ten get careful reads, by the hundredth it's "good enough"; the same resume scores 70 in the morning and 55 when the reviewer is tired after lunch; and the candidate's school, gender, or age quietly creep into judgment, making fairness hard to guarantee and harder to prove.

This n8n workflow funnels resume screening into one pipe: resumes arrive via Webhook or email attachment, text is extracted automatically, an LLM scores skill match, experience, stability, and risk against the JD and outputs a reason, an IF checks whether the tier clears the bar, then a Switch routes to interview queue / talent pool / rejection email, writing to a Feishu Bitable or ATS and notifying the recruiter along the way. A resume goes from submission to routing in minutes, and HR spends time only on the interview queue.

Compliance is the floor, not a bonus. The LLM Prompt explicitly forbids using protected attributes-gender, age, marital or parental status, race, household registration, disability, religion, political affiliation-as scoring factors; only skills, experience, and projects are weighed against the JD. Resumes carry personal information subject to China's Personal Information Protection Law (PIPL) and GDPR: get candidate authorization, minimize processing, set retention limits. AI scoring is assistive only; the final interview decision belongs to a human. Laws vary by jurisdiction-treat local law as authoritative.

Workflow Chain

Webhook trigger (careers-site form / email attachment / IM bot forward) -> Code node extracts text (PDF / Word parsed to plain text, formatting noise stripped) -> LLM scores against JD (skill_match / experience / stability / risk four dimensions, outputs score + tier + reason + suggested interview questions) -> IF clears bar (tier is interview or pool) -> Switch routes (interview / pool) -> write to Feishu Bitable or ATS -> notify recruiter; IF below bar (tier is reject) -> send rejection email -> status writeback.

This follows the same shape as our Lead Auto-Collect + Scoring + Notify Workflow (workflow-lead-crm-automation): leads and resumes are both "unstructured input arrives, score by rules, route by score" flows-the scoring dimensions swap to skill match and experience, and the routing exits swap to interview queue and ATS. It shares the "LLM extracts fields + writes to table + notifies" skeleton with our Customer Feedback Analysis Workflow (workflow-customer-feedback-analysis).

Download Template

Setup Steps

  1. Import: n8n -> Workflows -> Import from File, pick workflow-resume-screening.json
  2. Webhook node: copy the Production URL, wire it to your careers-site submission form or email-forwarding rule. Resumes can arrive as attachments or base64; the next node normalizes both
  3. Extract text node (Code): parse PDF / Word into plain text. n8n's built-in Extract from File node handles PDF; for Word, convert to .txt upstream or call a third-party parsing API. Strip header/footer dividers, email signatures, and special characters-keep only structured blocks: name, contact, education, work history, projects, skills. Generate a dedup key from contact info or name plus source
  4. LLM scoring node: fill in Kimi / DeepSeek / Qwen key; 8k context is enough. Prompt below; the model outputs skill_match (0-100), experience (0-100), stability (0-100), risk (low/medium/high), score (composite 0-100), tier (interview / pool / reject, one of three), reason (within 100 words), interview_questions (2-3 suggested follow-ups). Hard constraint: the Prompt explicitly forbids using gender / age / marital status / race / household registration as factors; the model looks only at skills and experience
  5. IF clears bar node: take tier equal to interview or pool into the pass branch; otherwise (reject) into the rejection branch. Threshold logic is encoded in the Prompt's tier definition: score>=80 interview, 60-79 pool, <60 reject. Tune by role tightness-raise the interview floor to 85 for core roles, drop to 50 for pipeline roles
  6. Switch route node: split the pass branch-tier interview to interview queue, pool to talent pool. The pool is not a rejection; it's "revisit when a slot opens"
  7. Write to Feishu / ATS node: HTTP Request to Feishu Bitable API or an ATS (Moka / Beisen / Greenhouse) write API. Fields: name, contact, score, risk, reason, interview_questions, source, submission time, route. This is the foundation for funnel analytics and post-mortems
  8. Send rejection email node: tier reject lands here. HTTP Request to an email API (Resend / Alibaba Cloud DirectMail), templated reply-thank them + a generalized mismatch reason (no specific score, no commentary on personal attributes) + notice that the resume is retained in the talent pool with an opt-out link. Run the wording by legal; don't promise "priority consideration in the future"
  9. Notify recruiter node: when the interview queue is hit, push a card via Slack / Feishu / DingTalk group bot carrying the candidate name, role, score, risk, and interview_questions so the recruiter can decide at a glance whether to grab a slot
  10. Status writeback node: HTTP Request PATCH back to the ATS, marking status=screened + score + tier + risk + screened_at. The closure of the loop-resumes no longer sit at "received" but carry a clear routing result
  11. Test run: manually submit three test resumes-one clearly matching, one borderline, one clearly mismatched-and verify they land in interview queue, talent pool, and rejection email respectively, that the Feishu table has records, and that the recruiter got the notification

Companion Prompt (LLM Resume Scoring by JD)

Prompt
You are a hiring assistant. Score the candidate's resume against the JD and output JSON:
- skill_match: 0-100, how well skills match JD keywords
- experience: 0-100, fit of years of experience and project history to role requirements
- stability: 0-100, job-hopping frequency and career continuity (frequent hops deduct)
- risk: low / medium / high, risk signals (long employment gaps, severe mismatch with JD, contradictions in the resume)
- score: composite 0-100
- tier: interview (score>=80) / pool (60-79) / reject (<60), pick one of three
- reason: within 100 words, cite facts from the resume
- interview_questions: 2-3 suggested follow-up questions for the interview
Rules: judge only on skills, experience, projects, and career history; forbid using gender, age, marital or parental status, race, household registration, disability, religion, or political affiliation as scoring factors-ignore any such information that appears in the resume; do not fabricate facts not in the resume; when info is severely insufficient, default score to 50, tier to pool, and prefix reason with [insufficient info].
JD: {{jd}}
Resume text: {{resume_text}}

Advanced

  • Dynamic thresholds: different roles and levels use different cutoffs. Store the tier score boundaries in a Feishu table or config center; the Code node fetches boundaries by role name, then the LLM outputs only the raw score and the Code node computes tier-so you don't republish the Prompt every time the threshold changes
  • Dedup anti-spam: same email or phone resubmitting in a short window-query the ATS first in the Code node; if a hit exists, update the score without creating a duplicate record, preventing score-farming and double rejections
  • Human-review layer: scores within 5 points of a boundary are a gray zone-don't route directly, queue for human review. These edge cases are the most valuable; accumulating them feeds back into Prompt tuning
  • Pre-generated interview questions: the LLM's interview_questions go straight to the interviewer, saving prep time. Flag that these are AI suggestions for reference only; the interviewer owns the actual assessment
  • Funnel analytics: all routing results land in Feishu Bitable, broken down by role, source, and score band-submissions / screened / interviewed / offered. "Which channel yields the highest-quality resumes" beats "which channel yields the most resumes"

Pitfalls

  • Pin anti-discrimination constraints in the Prompt: "score objectively" is not enough-protected attributes must be listed item by item as forbidden. By default the model will fold "35 years old," "female," "single" into consideration; without an explicit ban, discrimination goes implicit and is impossible to disprove after the fact. This is a compliance red line, not an optimization
  • Don't rely on a single parser: PDF resumes come in every shape; scanned PDFs return empty text from Extract from File. Add an IF on text length and route too-short results to an OCR fallback (call a cloud OCR API)-otherwise a matching candidate gets scored zero
  • Don't put specific scores in rejection emails: "Your profile was a lower match for this role" is enough; don't write "You scored 42." Specific scores are unprofessional and invite disputes
  • Mind retention limits by law: resumes carry ID numbers and home addresses-PIPL requires minimized processing and a clear retention period. Don't keep rejected resumes indefinitely; set 30-day or 6-month auto-delete per your jurisdiction. Pool candidates need an opt-in before retention, with an opt-out link
  • AI scoring is not the final say: score is assistive; the recruiter makes the interview call. Keep "AI recommendation" and "recruiter decision" as separate columns in the ATS and reconcile periodically-whether AI-approved candidates actually got hired, how AI-rejected candidates fared when interviewed anyway, all feeding back to calibrate the Prompt
  • Don't let the workflow replace communication: automation handles routing and logging, not decisions. For key roles, have a human eyeball the rejections too, to avoid false kills-an LLM scoring error on one resume costs a candidate months of waiting

References

This article is AI-assisted and human-edited. Last updated: 2026-08-04

Related

Field SOP

AI Digital Human Creation SOP: A Repeatable Workflow from Script to Final Cut

Breaks AI digital human creation into a six-step repeatable workflow: pick the tool by use case (HeyGen/D-ID/Synthesia/Colossyan/DeepBrain plus China's Tencent Zhiying/Guiji Intelligent), write the talking-head script (with prompt template), pick or customize the avatar, lock the voice before driving lip-sync, post-process subtitles/editing/compliance, and publish with platform adaptation. Includes 5 pitfalls (avatar licensing/lip-sync drift/multilingual voice/long-video cost/compliance labels) and 5 FAQs. Representative workflow, not a single-tool hands-on test; features subject to official sites.

Aug 7, 20268 min read