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

Qoder Free Credits Claim and Usage Management SOP

A hands-on SOP for claiming and managing Qoder's double promo: download and install (international qoder.com or China qoder.cn, across desktop, mobile, IDE, JetBrains plugin and CLI), sign up (the two editions keep separate accounts and quotas), confirm the free window works (selecting Qwen3.8-Flash in the model picker bills at a 0x coefficient, nothing to claim), then the daily 100 Credits rhythm (opens 10:00 daily, one claim per cycle, no carryover of missed days, each grant valid 30 days and stackable), usage management (check burn in the usage panel, let Qwen3.8-Flash carry routine work and save Credits for hard tasks), deduction rules (earliest-expiring credits are consumed first, in-plan before add-on packs on the same day), and a closing plan for when the window ends on September 30. UI details follow the actual client.

Sep 18, 20268 min read
Field SOP

Octop Self-Hosted AI Assistant Deployment SOP

A hands-on SOP for deploying Octop: it starts with a decision framework on whether to self-host at all, then walks four install paths (one-line script, Windows PowerShell, Docker Compose, and the Tencent Cloud Lighthouse or CVM official image marketplace), runs octop init and octop run verbatim from the official README (default port 8088), changes default credentials on first login (the README hardcodes none, third-party reviews report admin/octop, and Docker init generates a random one), then configures models (OpenAI-compatible, Ollama, nearly 20 providers), experts with MBTI personas, connectors (Tencent Docs, OAuth, MCP) and IM channels, and closes with Docker Compose and PostgreSQL productionization plus a six-item pitfall table and a ten-item pre-launch checklist.

Sep 17, 20268 min read
Field SOP

Intern-S2 in practice: from free API to scientific workflows

A hands-on SOP for accessing Intern-S2: for individuals and small teams the realistic path is the free API (chat.intern-ai.org.cn for online use, internlm.intern-ai.org.cn/api/strategy for quota), while institutions with compute can run the HuggingFace weights at internlm/Intern-S2-397B. It gives a three-way access comparison table, a minimal runnable Python call for the free API, an HF inference skeleton, two copy-paste prompt templates for scientific long-horizon tasks (molecule binder design, materials structure generation), plus Memory Decoder mounting notes and a ten-item pitfall list (free-tier rate limits, 397B out-of-memory, long-context truncation, the Preview model's 2026-10-31 shutdown and migration). Bottom line: start free on the API, do not jump straight to self-hosting a 397B model.

Sep 17, 202611 min read