Project
Job Application Pipeline
n8n workflow that scores a job URL, drafts a package for strong fits, and stops for a human
- Role
- Solo
- Focus
- Structured LLM output, Human review, No auto-apply
- Testing
- Eval fixtures, Fit-score threshold, Digest does not apply
- Key patterns
- n8n, OpenAI, Structured JSON, Google Sheets
- n8n
- Docker
- OpenAI
- Google Sheets
- Gmail
- TypeScript
What it does
- Takes a job URL, fetches the posting, and scores it against a fixed résumé profile
- Returns structured JSON: fit score, stack match and gaps, live-coding risk, and apply priority
- Drafts a cover letter, bullets, and a short recruiter note only when the score clears 7
- Appends the result to a Google Sheet and emails a digest for human review
- A second workflow lists still-open drafts on weekday mornings and evenings and does not apply
Architecture
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Job URL form │────►│ Fetch posting │────►│ Fit score LLM │
│ jobUrl + notes │ │ HTML → text │ │ structured JSON │
└──────────────────┘ └──────────────────┘ └────────┬─────────┘
│
score < 7 │ score ≥ 7
│ ▼
│ ┌──────────────────┐
│ │ Draft package │
│ │ letter + bullets│
│ └────────┬─────────┘
▼ ▼
┌──────────────────────────────────────────────┐
│ Google Sheets status = draft_ready │
└──────────────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Gmail digest you review, never applies │
└──────────────────────────────────────────────┘Reliability and guardrails
- Prompts only use the posting plus a locked résumé profile — no invented employers, metrics, or skills
- Parse nodes turn Continue On Fail off so bad JSON stops the run instead of writing a half row
- Eval fixtures cover a strong .NET/React match, a stack mismatch, and a live-coding flag
- The digest workflow only reads the sheet. It does not change status and it does not call the model
- Nothing in either workflow submits an application
Key engineering decisions
Local Docker n8n instead of a hosted runner
The workflow exports as JSON, credentials stay off the repo, and the demo costs nothing to host.
Cheap model for the fit score, stronger model for the draft
Scoring is a structured JSON pass. The cover letter only runs when fitScore is 7 or higher.
Google Sheets plus email, not a database
The row is the review queue. A separate weekday workflow reads draft_ready rows and never writes them.
Failure modes handled
- OpenAI returns nested or fenced JSON — a Code node flattens output text and JSON.parse fails loud
- Fit score under 7 — the draft step is skipped and nothing is emailed to an employer
- No draft_ready rows — the digest workflow sends no email
- Laptop asleep or n8n stopped — that digest slot is missed and the next weekday run lists the same open drafts
- Posting asks for live coding — liveCodingRisk is set to likely so the review shows it
How to demo in 2 minutes
- 1docker compose up -d and open n8n at http://localhost:5678
- 2Import n8n/workflow.json and n8n/digest-workflow.json, then reconnect OpenAI, Sheets, and Gmail
- 3Open the form URL and paste a job posting
- 4Check the Applications sheet for the fit score and, at 7 or above, the draft
- 5Confirm the weekday digest only lists draft_ready rows and that no employer is emailed