Most job seekers send the same generic resume to every application. Recruiters can tell immediately — and so can ATS systems, which quietly reject resumes that don't match the job description's exact keyword patterns. The fix is tailoring your resume for each role, but who has time to rewrite bullet points for every application?
This n8n workflow automates the entire tailoring process. You paste in a job description (or send it via webhook), and within a minute you get back a GPT-4-rewritten version of your resume with matched keywords, rephrased bullet points that mirror the job's language, a gap analysis, and an ATS score estimate. No more manual rewriting — just review, approve, and apply.
⚡ What You'll Build
- ✅ On-demand resume tailoring triggered by webhook or manual run
- ✅ Automatic job description parsing and requirements extraction
- ✅ GPT-4 gap analysis: what you have vs. what the job needs
- ✅ Intelligent bullet point rewriter matching the job's language
- ✅ ATS keyword injection and skills section optimization
- ✅ Tailored resume saved to Google Drive + emailed to you
Prerequisites
You'll need these accounts before building. All have free tiers that are more than sufficient for personal use:
- n8n account: Free cloud at n8n.io or self-hosted
- OpenAI API key: From platform.openai.com (~$0.05 per resume tailoring run with GPT-4o)
- Google Drive + Gmail: For storing your base resume and emailing results
- Your resume as a PDF: Uploaded to Google Drive with text-based (not scanned) content
How the Workflow Works
The workflow is triggered on demand — either manually in n8n or via a webhook you can call from a browser bookmark or Zapier. Here's the 8-step pipeline:
- Webhook / Manual Trigger → receives the job description text
- Job Description Parser → extracts structured requirements from raw text
- Resume Download → fetches your base resume PDF from Google Drive
- PDF Text Extractor → converts PDF binary to plain text
- AI Gap Analyzer → GPT-4 identifies missing keywords and experience gaps
- AI Resume Rewriter → GPT-4 rewrites bullet points to match job language
- ATS Keyword Optimizer → injects missing keywords into skills/summary sections
- Save & Email → saves tailored resume to Drive and emails it to you
Step-by-Step Workflow Setup
Open your n8n workspace and create a new workflow named "AI Resume Tailor." Add each node below in sequence.
Step 1: Webhook Trigger
🔗 Webhook Node — Receive Job Description
Creates an HTTP endpoint you can call from anywhere — a browser extension, Zapier, or a simple curl command — to kick off the tailoring process with a job description payload.
Add a Webhook node and configure:
- HTTP Method: POST
- Path:
tailor-resume - Response Mode: Last Node (returns the tailored output)
- Authentication: Header Auth with a secret token (optional but recommended)
The webhook URL will be: https://your-n8n-instance.com/webhook/tailor-resume. You'll POST a JSON body like {"job_description": "We are looking for a Senior Python Engineer..."} to trigger the workflow. During development, use the Manual Trigger node instead and hardcode a sample job description in a Code node immediately after it.
Pro tip: Create a browser bookmarklet that captures selected text from a job posting page and POSTs it to your webhook URL. One click on any job listing instantly starts the tailoring process.
Step 2: Job Description Parser
🔧 Code Node — Parse Job Requirements
Cleans the raw job description text and prepares it for the AI nodes. Handles common formatting issues like extra whitespace, HTML artifacts, and emoji characters that could confuse the language model.
const rawJD = $input.first().json.body?.job_description
|| $input.first().json.job_description
|| '';
// Clean up the text
const cleaned = rawJD
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\s{3,}/g, '\n\n')
.trim();
if (!cleaned || cleaned.length < 50) {
throw new Error('Job description too short or missing. Please provide at least 50 characters.');
}
return [{ json: {
job_description: cleaned,
jd_length: cleaned.length,
timestamp: new Date().toISOString()
}}];
Step 3: Resume Download from Google Drive
📥 Google Drive Node — Fetch Base Resume
Downloads your master resume PDF from Google Drive. Using Drive as the source means any manual updates you make to your resume are instantly picked up on the next run — no workflow changes needed.
Add a Google Drive node:
- Operation: Download File
- File ID: The ID from your resume's Google Drive URL
- Credentials: OAuth2 (connect your Google account)
Keep a single "master resume" in Drive — your most complete, unedited version with all experience. This workflow always tailors a fresh copy of the master, so you never accidentally overwrite your original.
Step 4: PDF Text Extraction
📄 Extract from File Node — PDF to Text
Converts the binary PDF data into plain text that the AI can read and analyze. The quality of this extraction directly affects tailoring quality — a well-formatted PDF produces much better results.
Add an Extract from File node:
- Operation: Extract Text from PDF
- Binary Property:
data
After extracting, the output JSON will have a text field with your full resume content. Verify the extraction preserves your formatting reasonably well — section headers, job titles, dates, and bullet points should all be readable. If your PDF was created from a scanned document, the text will be garbled; re-export from Word/Google Docs to create a proper text-based PDF.
Step 5: AI Gap Analysis
🤖 OpenAI Node — Gap Analyzer
The first AI pass. GPT-4 reads both your resume and the job description, then produces a structured gap analysis: which required skills you already demonstrate, which are missing, which experiences should be emphasized, and what the ATS keyword density looks like.
Add an OpenAI node:
- Model: gpt-4o (faster and cheaper than gpt-4-turbo, same quality for this task)
- Max Tokens: 1000
- Temperature: 0.2 (analytical, consistent output)
System prompt:
User message:
Step 6: AI Resume Rewriter
✍️ OpenAI Node — Resume Rewriter
The core transformation step. GPT-4 rewrites your resume using the gap analysis from Step 5 as instructions. It preserves all factual information while rephrasing bullet points to mirror the job description's language and emphasizing the most relevant experiences.
Add a Code node first to parse the gap analysis JSON, then another OpenAI node:
// Parse gap analysis from previous node
const raw = $input.first().json.message?.content || $input.first().json.choices?.[0]?.message?.content || '';
let gap;
try {
gap = JSON.parse(raw);
} catch(e) {
// If JSON parse fails, extract what we can
gap = { missing_keywords: [], bullets_to_rewrite: [], recommended_emphasis: [] };
}
return [{ json: { gap_analysis: gap, raw_gap: raw } }];
Then add the Rewriter OpenAI node:
- Model: gpt-4o
- Max Tokens: 3000
- Temperature: 0.4
Step 7: ATS Keyword Optimizer
🎯 OpenAI Node — ATS Keyword Finalizer
A targeted final pass that focuses solely on the skills section and technical keywords. Ensures all missing high-priority keywords from the gap analysis are present in the resume — particularly in the skills section, which ATS systems parse with highest weight.
Step 8: Save to Drive & Email
📧 Gmail + Google Drive — Deliver Results
Saves the tailored resume text to a new Google Drive file and emails you both the tailored content and the gap analysis summary. You get everything you need to apply in your inbox within 60 seconds of triggering the workflow.
Add a Code node to build the email HTML, then a Gmail node:
const tailoredResume = $('ATS Optimizer').first().json.message.content;
const gap = $('Parse Gap Analysis').first().json.gap_analysis;
const jobDesc = $('Job Description Parser').first().json.job_description.slice(0, 200);
const html = `
<h2>Your Tailored Resume is Ready</h2>
<p><strong>Job Snippet:</strong> ${jobDesc}...</p>
<p><strong>Match Score:</strong> ${gap.match_score || 'N/A'}% → optimized with tailoring</p>
<p><strong>ATS Risk:</strong> ${gap.ats_risk || 'N/A'}</p>
<p><strong>Keywords Added:</strong> ${(gap.missing_keywords || []).slice(0,8).join(', ')}</p>
<hr>
<h3>Tailored Resume (plain text — paste into your template)</h3>
<pre style="white-space:pre-wrap;font-family:monospace;background:#f5f5f5;padding:20px;border-radius:8px;">
${tailoredResume.replace(/</g, '<').replace(/>/g, '>')}
</pre>
`;
return [{ json: { email_html: html, tailored_resume: tailoredResume } }];
Configure the Gmail node:
- Operation: Send Email
- To: your email address
- Subject:
📄 Tailored Resume Ready — {{ new Date().toLocaleDateString() }} - Email Type: HTML
- HTML Body:
={{ $json.email_html }}
Tips & Customizations
Use gpt-4o-mini to cut costs
If you're tailoring many resumes per week, swap gpt-4o for gpt-4o-mini on the gap analysis and ATS optimizer steps. Keep gpt-4o only for the rewriter step. This reduces per-run costs from ~$0.08 to ~$0.02 while maintaining most of the quality where it matters most.
Add a Google Docs output
Instead of (or in addition to) emailing plain text, use the Google Docs node to create a new document with the tailored resume content. This makes formatting and final touch-ups much easier than working with a plain text email.
Build a version history
Add a Google Sheets node that appends each tailoring run: date, company name (extracted from the JD with regex), match score before/after, and a Drive link to the tailored resume. Over time, this shows which roles you're targeting most and how your resume evolves.
Create a browser extension trigger
With n8n's webhook URL, you can build a simple browser extension that extracts the full text of any job posting page and sends it to your workflow in one click. The Chrome Extensions documentation has a minimal boilerplate for content scripts that can select and POST page text.