Automatisation de la recherche d'emploi et de la personnalisation des CV avec Mistral AI, LinkedIn et Google Sheets
Ceci est unPersonal Productivity, AI Summarizationworkflow d'automatisation du domainecontenant 46 nœuds.Utilise principalement des nœuds comme Set, Code, Html, Sort, Wait. Automatisation de la recherche d'offres d'emploi et de la personnalisation de CV avec Mistral AI, LinkedIn et Google Sheets
- •Compte Google et informations d'identification Gmail API
- •Informations d'identification Google Drive API
- •Peut nécessiter les informations d'identification d'authentification de l'API cible
- •Informations d'identification Google Sheets API
Nœuds utilisés (46)
Catégorie
{
"meta": {
"instanceId": "b8bca2081b6c394c24ae4b81e9aa6d613d549c27564693b18550c263bcbb0c03",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "e08473bb-0b75-4ceb-81c0-92be71165fb4",
"name": "Mistral Cloud Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatMistralCloud",
"position": [
-1968,
2048
],
"parameters": {
"model": "mistral-small-latest",
"options": {}
},
"typeVersion": 1
},
{
"id": "6716f4ea-2529-4665-bdfa-8943a8cd5ffc",
"name": "HTML2",
"type": "n8n-nodes-base.html",
"position": [
-1056,
2368
],
"parameters": {
"options": {},
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "Title",
"cssSelector": "div h1"
},
{
"key": "Company",
"cssSelector": "div span a"
},
{
"key": "Location",
"cssSelector": "div span[class*='topcard__flavor topcard__flavor--bullet']"
},
{
"key": "Description",
"cssSelector": "div.description__text.description__text--rich"
},
{
"key": "Job ID",
"attribute": "data-semaphore-content-urn",
"cssSelector": "a[data-item-type='semaphore']",
"returnValue": "attribute"
},
{
"key": "Salary",
"cssSelector": "div.salary.compensation__salary"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "01e0b4e5-4b4d-4102-8a67-a1c0ab90717a",
"name": "Split Out",
"type": "n8n-nodes-base.splitOut",
"position": [
-880,
1920
],
"parameters": {
"options": {},
"fieldToSplitOut": "jobs"
},
"typeVersion": 1
},
{
"id": "7214a29b-f4d2-456a-a77e-15938361a61e",
"name": "Boucler sur les éléments",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-400,
2080
],
"parameters": {
"options": {}
},
"executeOnce": true,
"typeVersion": 3
},
{
"id": "244d669c-b275-47ce-ade8-bf33dd5761f5",
"name": "Send a message",
"type": "n8n-nodes-base.gmail",
"position": [
528,
2000
],
"webhookId": "2a252ec0-2b36-4a2a-a502-9adf5e5c9752",
"parameters": {
"sendTo": "[YOUR_EMAIL_ADDRESS]",
"message": "=<p style=\"font-family: Arial, sans-serif; color: #333333;\">\\n Dear Job Seeker,<br><br>\\n This daily digest summarizes the results of your automated job search. The Top 5 job matches found in today's run are detailed below, with comprehensive analysis available in your tracking sheet.\\n</p>\\n\\n<div style=\"margin-top: 20px;\">\\n <h3 style=\"font-family: Arial, sans-serif; color: #0077b5; border-bottom: 2px solid #eeeeee; padding-bottom: 5px;\">\\n 🏆 Top 5 Job Matches\\n </h3>\\n\\n {{ $json.data.map((item, index) => {\\n // Safely replace newlines in Improvements\\n const improvements = item.Improvements ? item.Improvements.replace(/\\n/g, '<br>').replace(/\"/g, '"') : 'N/A';\\n \\n // Choose alternating background color\\n const bgColor = index % 2 === 0 ? '#f4f4f4' : '#ffffff';\\n \\n // Determine score color for visual emphasis\\n const score = parseInt(item.Score) || 0;\\n const scoreColor = score >= 80 ? 'green' : (score >= 60 ? 'orange' : 'red');\\n\\n // Extract the first improvement point\\n const topImprovement = improvements.split('<br>')[0] || 'No specific top improvement listed.';\\n\\n return `\\n <div style=\"border: 1px solid #dddddd; padding: 15px; margin-bottom: 20px; border-radius: 8px; background-color: ${bgColor};\">\\n <h4 style=\"margin-top: 0; color: #333333;\">\\n ${index + 1}. ${item.Title} @ ${item.Company}\\n </h4>\\n <p style=\"margin: 5px 0; font-family: Arial, sans-serif; font-size: 14px;\">\\n <strong>Match Score:</strong> <strong style=\"color: ${scoreColor}; font-weight: bold;\">${item.Score}/100</strong>\\n | <strong>Location:</strong> ${item.Location}\\n | <strong>Salary:</strong> ${item['Salary '] || 'N/A'}\\n </p>\\n <p style=\"margin: 10px 0; font-family: Arial, sans-serif;\">\\n <a href=\"${item.Link}\" style=\"color: #0077b5; text-decoration: none; font-weight: bold;\">[View Job & Apply]</a>\\n </p>\\n \\n <div style=\"margin-top: 15px; padding: 10px; border-top: 1px dashed #cccccc;\">\\n <h5 style=\"color: #555555; margin-bottom: 5px; font-size: 14px;\">Top Resume Improvement Action:</h5>\\n <span style=\"font-size: 13px; color: #555; white-space: pre-wrap;\">${topImprovement}</span>\\n </div>\\n </div>\\n `;\\n }).join('') }}\\n</div>\\n\\n<p style=\"font-family: Arial, sans-serif; color: #333333; margin-top: 30px;\">\\n Access the full, filterable list and detailed notes for all jobs (including full cover letters, red flags, and more) here:<br>\\n <a href=\"[YOUR_GOOGLE_SHEET_LINK]\" style=\"color: #0077b5; text-decoration: none; font-weight: bold; font-size: 16px; display: inline-block; margin-top: 8px;\">\\n 🚀 Open Job Search Tracking Sheet\\n </a>\\n</p>\\n<p style=\"font-family: Arial, sans-serif; color: #333333; margin-top: 20px;\">\\n Best of luck with your job search!\\n</p>",
"options": {
"appendAttribution": false
},
"subject": "Job Match Results"
},
"typeVersion": 2.1
},
{
"id": "e1a36beb-28ad-40c6-b534-28c9e84a5b9c",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-1824,
2048
],
"parameters": {
"jsonSchemaExample": "{\n\t\"keyword\": \"Maintenance Manager\",\n\t\"location\": \"Los Angeles\",\n\t\"experiencelevel\": \"Mid-Senior Level\",\n\t\"remote\": \"No\",\n\t\"alternatekeyword\": \"Engineering Manager\",\n\t\"alternatekeyword1\": \"Engineering Manager\",\n\t\"alternatekeyword2\": \"Engineering Manager\"\n\n}"
},
"typeVersion": 1.3
},
{
"id": "933481cf-5018-489b-8679-43faf6c21791",
"name": "Fusionner",
"type": "n8n-nodes-base.merge",
"position": [
-1248,
1888
],
"parameters": {
"numberInputs": 4
},
"typeVersion": 3.2
},
{
"id": "92eda0a2-50f8-43e8-946b-52014ad79eeb",
"name": "Mistral Cloud Chat Model4",
"type": "@n8n/n8n-nodes-langchain.lmChatMistralCloud",
"position": [
-192,
2624
],
"parameters": {
"model": "mistral-large-latest",
"options": {}
},
"typeVersion": 1
},
{
"id": "65b3a2cd-d78b-4f44-9bd1-16be870bf431",
"name": "Resume Breakdown1",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-1968,
1840
],
"parameters": {
"text": "=You are a resume review AI Agent.\\n\\nUse {{ $json.text }} to provide the following outputs;\\n-Keyword, main job title. Pick the top single job title based on this resume. This must not include the word \"and\". Focus on the industry or job area relevant in the resume.\\n-Alternate Keyword, list other possible job titles. Only list the top 3. This must not include the word \"and\". \\n-Location of Job Desired, if no desired location is provided use the current address city.\\n-Experience Level, Pick one of the following Internship, Entry Level, Associate, Mid-Senior Level, Director, Executive\\n-Remote, Choose one, On-Site, Hybrid, Remote. If not specified respond \"On-Site\".",
"options": {},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2.2
},
{
"id": "7a0078d7-8566-465c-ae95-6287ec352ca3",
"name": "Job Matching Agent IA1",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-192,
2208
],
"parameters": {
"text": "=You are a precise job-matching and content generation expert. Your task is to analyze a resume against a job description, compute a match score, provide actionable feedback, and draft a cover letter.\\n\\nReturn **EXACTLY ONE JSON** object wrapped in ```json fences, followed by the line END_OF_JSON. Do NOT include any introductory or concluding prose.\\n\\nINPUT DATA\\njob_description: {{ $json.Description }}\\nmy_resume: {{ $('Extract from Resume1').item.json.text }}\\n\\nTASKS\\n1) JOB ANALYSIS: Extract and structure the following data from the job_description.\\n2) RESUME ANALYSIS: Extract and structure the following data from the my_resume.\\n3) MATCH SCORING (integer 0–100): Calculate the score based on the weighted categories provided below. Deduct up to 10 points for issues listed in red_flags.\\n - Skills/Tools overlap: 40 points\\n - Relevant experience & seniority: 25 points\\n - Responsibilities alignment: 15 points\\n - Education/Certs fit: 10 points\\n - Domain/industry fit: 5 points\\n - Logistics (location/work auth/availability): 5 points\\n4) SCORE EXPLANATION: For each category, provide 1–3 *concise* evidence bullets. Quote short, relevant fragments (escape quotes) and cite \"JD\" or \"Resume\".\\n5) GAPS & SUGGESTIONS: Identify missing/weak requirements and list 1–2 concrete upskilling steps for each.\\n6) COVER LETTER: Generate a professional, highly-tailored cover letter (150–220 words, 2–4 paragraphs). Focus on concrete impacts. **Must use JSON-safe formatting:** escape all double quotes as \\\" and use \\n for newlines. Omit greeting and signature.\\n\\nSTRICT FORMATTING RULES\\n- All array elements must be **short phrases (≤ 140 characters)**, single line, with NO markdown, line breaks, asterisks, or list indicators. Summarize long paragraphs.\\n- Ensure all key-value pairs adhere strictly to the schema below.\\n\\nSCHEMA\\n```json\\n{\\n \\\"job_analysis\\\": {\\n \\\"title\\\": \\\"\\\",\\n \\\"company\\\": \\\"\\\",\\n \\\"must_have_skills\\\": [],\\n \\\"nice_to_have_skills\\\": [],\\n \\\"responsibilities\\\": [],\\n \\\"years_of_experience\\\": \\\"\\\",\\n \\\"education_certifications\\\": \\\"\\\",\\n \\\"location_constraints\\\": \\\"\\\",\\n \\\"domain_industry_focus\\\": \\\"\\\",\\n \\\"tech_stack\\\": [],\\n \\\"measurable_kpis\\\": [],\\n \\\"relocation_requirement\\\": \\\"\\\"\\n },\\n \\\"resume_analysis\\\": {\\n \\\"core_skills\\\": [],\\n \\\"tools_tech\\\": {\\n \\\"programming_languages\\\": [],\\n \\\"frontend_technologies\\\": [],\\n \\\"backend_technologies\\\": [],\\n \\\"databases_devops\\\": []\\n },\\n \\\"years_of_experience_key_areas\\\": {},\\n \\\"accomplishments_with_metrics\\\": [],\\n \\\"education_certs\\\": [],\\n \\\"domains\\\": [],\\n \\\"roles_titles\\\": [],\\n \\\"leadership_collaboration\\\": [],\\n \\\"location_work_auth\\\": \\\"\\\"\\n },\\n \\\"match_score\\\": 0,\\n \\\"score_explanation\\\": [\\n { \\\"category\\\": \\\"Skills/Tools overlap (40 points)\\\", \\\"score\\\": 0, \\\"evidence\\\": [] },\\n { \\\"category\\\": \\\"Relevant experience depth & seniority (25 points)\\\", \\\"score\\\": 0, \\\"evidence\\\": [] },\\n { \\\"category\\\": \\\"Responsibilities alignment (15 points)\\\", \\\"score\\\": 0, \\\"evidence\\\": [] },\\n { \\\"category\\\": \\\"Education/Certs fit (10 points)\\\", \\\"score\\\": 0, \\\"evidence\\\": [] },\\n { \\\"category\\\": \\\"Domain/industry fit (5 points)\\\", \\\"score\\\": 0, \\\"evidence\\\": [] },\\n { \\\"category\\\": \\\"Logistics (location, work auth, availability) (5 points)\\\", \\\"score\\\": 0, \\\"evidence\\\": [] }\\n ],\\n \\\"red_flags\\\": [],\\n \\\"gaps_and_suggestions\\\": [\\n { \\\"gap\\\": \\\"\\\", \\\"suggestion\\\": \\\"\\\" }\\n ],\\n \\\"cover_letter\\\": \\\"\\\"\\n}",
"options": {},
"promptType": "define"
},
"typeVersion": 2.2
},
{
"id": "676d5633-9739-4c84-8607-836941b42162",
"name": "Resume Analysis Agent IA1",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-192,
2416
],
"parameters": {
"text": "=You are a ruthless resume editor. Your sole output must be a crisp, numbered list of actionable resume changes to maximize job fit.\\n\\nInputs:\\n\\njob_description: {{ $json.Description }}\\n\\nmy_resume: {{ $('Extract from Resume1').item.json.text }}\\n\\nSTRICT OUTPUT RULES:\\n\\nOutput a numbered list only; start with the highest-impact change.\\n\\nEach point must be a single line of 14 words or less.\\n\\nEach line must start with one of the following tags: [ADD], [REMOVE], [REWRITE], [ORDER], [QUANTIFY], [KEYWORDS], [FORMAT], [FOCUS].\\n\\nAll points must be based on genuine gaps against the job_description. Do not invent experience.\\n\\nInclude a final, single line for keywords: 'Missing keywords: term1, term2, ...' (list only if any).\\n\\nNo intros, explanations, code fences, bullet points, or any extra text.",
"options": {},
"promptType": "define"
},
"typeVersion": 2.2
},
{
"id": "5f720ba5-cccd-4428-a948-d74dbd02e9ba",
"name": "Get row(s) in Job Search1",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1024,
1584
],
"parameters": {
"options": {},
"sheetName": "[YOUR_SHEET_NAME]",
"documentId": "[YOUR_SPREADSHEET_ID]"
},
"executeOnce": true,
"typeVersion": 4.7
},
{
"id": "6d15572d-5eb3-4959-b8ae-df09c4746d27",
"name": "Append or update row in Job Search1",
"type": "n8n-nodes-base.googleSheets",
"position": [
112,
2416
],
"parameters": {
"columns": {
"value": {
"Link": "={{ $('Edit Fields2').item.json['Apply Link'] }}",
"Score": "={{ $('Edit Fields3').item.json.match_score }}",
"Title": "={{ $('Edit Fields3').item.json.job_analysis.title }}",
"Skills": "={{ $('Edit Fields3').item.json.resume_analysis.core_skills.join('\\n') }}",
"Company": "={{ $('Edit Fields2').item.json.Company }}",
"Salary ": "={{ $('Edit Fields2').item.json.Salary }}",
"Location": "={{ $('Edit Fields2').item.json.Location }}",
"Red Flags": "={{ $('Edit Fields3').item.json.red_flags.map((flag, index) => `${index + 1}. ${flag}`).join('\\n') }}",
"Cover Letter": "={{ $('Edit Fields3').item.json.cover_letter }}",
"Improvements": "={{ $json.output }}",
"Relocation Requirement": "={{ $('Edit Fields3').item.json.job_analysis.relocation_requirement }} "
},
"schema": [
{
"id": "Link",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Link",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Title",
"type": "string",
"display": true,
"required": false,
"displayName": "Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Company",
"type": "string",
"display": true,
"required": false,
"displayName": "Company",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Location",
"type": "string",
"display": true,
"required": false,
"displayName": "Location",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Score",
"type": "string",
"display": true,
"required": false,
"displayName": "Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Cover Letter",
"type": "string",
"display": true,
"required": false,
"displayName": "Cover Letter",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Skills",
"type": "string",
"display": true,
"required": false,
"displayName": "Skills",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Improvements",
"type": "string",
"display": true,
"required": false,
"displayName": "Improvements",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Red Flags",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Red Flags",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Relocation Requirement",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Relocation Requirement",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Salary ",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Salary ",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Link"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": "[YOUR_SHEET_NAME]",
"documentId": "[YOUR_SPREADSHEET_ID]"
},
"typeVersion": 4.7
},
{
"id": "fe0bc527-e772-4c99-9566-aef4cadefc55",
"name": "Extract from Resume1",
"type": "n8n-nodes-base.extractFromFile",
"position": [
-2112,
1840
],
"parameters": {
"options": {},
"operation": "pdf"
},
"typeVersion": 1
},
{
"id": "571dd21c-635a-4198-9013-37670859bcf0",
"name": "Download Resume1",
"type": "n8n-nodes-base.googleDrive",
"position": [
-1872,
1584
],
"parameters": {
"fileId": "[YOUR_RESUME_DRIVE_URL]",
"options": {},
"operation": "download"
},
"typeVersion": 3
},
{
"id": "90041e96-9f01-497c-bea3-1c8cb1d4cf1d",
"name": "Déclencheur planifié1",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-2048,
1584
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 12,
"triggerAtMinute": 45
}
]
}
},
"typeVersion": 1.2
},
{
"id": "65c5c979-8b77-47c5-856b-68a6d8e24203",
"name": "Compare Datasets1",
"type": "n8n-nodes-base.compareDatasets",
"position": [
-624,
2000
],
"parameters": {
"options": {},
"fuzzyCompare": true,
"mergeByFields": {
"values": [
{
"field1": "Link",
"field2": "Apply Link"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "a80f67f7-8dde-45f7-ba11-74db416877ea",
"name": "LinkedIn Search URL",
"type": "n8n-nodes-base.code",
"position": [
-1616,
1696
],
"parameters": {
"jsCode": "let url = \"https://www.linkedin.com/jobs/search/?f_TPR=r86400\"\\n\\nconst keyword = $input.first().json.output.keyword\\nconst location = $input.first().json.output.location\\nconst experienceLevel = $input.first().json.output.experiencelevel\\nconst remote = $input.first().json.output.remote\\n\\n\\n\\nif (keyword != \"\") {\\n url += `&keywords=${keyword}`;\\n}\\n\\nif (location != \"\") {\\n url += `&location=${location}`;\\n}\\n\\nif (experienceLevel !== \"\") {\\n // Transform experience levels to LinkedIn codes\\n // Internship -> 1, Entry level -> 2, Associate -> 3\\n // Mid-Senior level -> 4, Director -> 5, Executive -> 6\\n const transformedExperiences = experienceLevel\\n .split(\",\")\\n .map((exp) => {\\n switch (exp.trim()) {\\n case \"Director\": return \"5\";\\n case \"Mid-Senior Level\": return \"4\";\\n case \"Internship\": return \"1\";\\n case \"Entry Level\": return \"2\";\\n case \"Associate\": return \"3\";\\n default: return \"\";\\n }\\n })\\n .filter(Boolean);\\n url += `&f_E=${transformedExperiences.join(\",\")}`;\\n}\\n\\nif (remote.length != \"\") {\\n // Transform remote options to LinkedIn codes\\n // On-Site -> 1, Remote -> 2, Hybrid -> 3\\n const transformedRemote = remote\\n .split(\",\")\\n .map((e) => {\\n switch (e.trim()) {\\n case \"Remote\": return \"2\";\\n case \"Hybrid\": return \"3\";\\n case \"On-Site\": return \"1\";\\n default: return \"\";\\n }\\n })\\n .filter(Boolean);\\n url += `&f_WT=${transformedRemote.join(\",\")}`;\\n}\\n\\n\\n\\n\\nreturn {url}\\n"
},
"typeVersion": 2
},
{
"id": "361256c2-5e01-4195-bc02-c8d7071a9356",
"name": "LinkedIn Search URL5",
"type": "n8n-nodes-base.code",
"position": [
-1616,
1840
],
"parameters": {
"jsCode": "let url = \"https://www.linkedin.com/jobs/search/?f_TPR=r86400\"\\n\\nconst keyword = $input.first().json.output.alternatekeyword\\nconst location = $input.first().json.output.location\\nconst experienceLevel = $input.first().json.output.experiencelevel\\nconst remote = $input.first().json.output.remote\\n\\n\\n\\nif (keyword != \"\") {\\n url += `&keywords=${keyword}`;\\n}\\n\\nif (location != \"\") {\\n url += `&location=${location}`;\\n}\\n\\nif (experienceLevel !== \"\") {\\n // Transform experience levels to LinkedIn codes\\n // Internship -> 1, Entry level -> 2, Associate -> 3\\n // Mid-Senior level -> 4, Director -> 5, Executive -> 6\\n const transformedExperiences = experienceLevel\\n .split(\",\")\\n .map((exp) => {\\n switch (exp.trim()) {\\n case \"Director\": return \"5\";\\n case \"Mid-Senior Level\": return \"4\";\\n case \"Internship\": return \"1\";\\n case \"New Grad\": return \"3\";\\n default: return \"\";\\n }\\n })\\n .filter(Boolean);\\n url += `&f_E=${transformedExperiences.join(\",\")}`;\\n}\\n\\nif (remote.length != \"\") {\\n // Transform remote options to LinkedIn codes\\n // On-Site -> 1, Remote -> 2, Hybrid -> 3\\n const transformedRemote = remote\\n .split(\",\")\\n .map((e) => {\\n switch (e.trim()) {\\n case \"Remote\": return \"2\";\\n case \"Hybrid\": return \"3\";\\n case \"On-Site\": return \"1\";\\n default: return \"\";\\n }\\n })\\n .filter(Boolean);\\n url += `&f_WT=${transformedRemote.join(\",\")}`;\\n}\\n\\n\\n\\nreturn {url}\\n"
},
"typeVersion": 2
},
{
"id": "454faa82-eee8-4faa-b9e9-c6832452fbc6",
"name": "LinkedIn Search URL6",
"type": "n8n-nodes-base.code",
"position": [
-1616,
1984
],
"parameters": {
"jsCode": "let url = \"https://www.linkedin.com/jobs/search/?f_TPR=r86400\"\\n\\nconst keyword = $input.first().json.output.alternatekeyword1\\nconst location = $input.first().json.output.location\\nconst experienceLevel = $input.first().json.output.experiencelevel\\nconst remote = $input.first().json.output.remote\\n\\n\\n\\nif (keyword != \"\") {\\n url += `&keywords=${keyword}`;\\n}\\n\\nif (location != \"\") {\\n url += `&location=${location}`;\\n}\\n\\nif (experienceLevel !== \"\") {\\n // Transform experience levels to LinkedIn codes\\n // Internship -> 1, Entry level -> 2, Associate -> 3\\n // Mid-Senior level -> 4, Director -> 5, Executive -> 6\\n const transformedExperiences = experienceLevel\\n .split(\",\")\\n .map((exp) => {\\n switch (exp.trim()) {\\n case \"Director\": return \"5\";\\n case \"Mid-Senior Level\": return \"4\";\\n case \"Internship\": return \"1\";\\n case \"Entry Level\": return \"2\";\\n case \"New Grad\": return \"3\";\\n default: return \"\";\\n }\\n })\\n .filter(Boolean);\\n url += `&f_E=${transformedExperiences.join(\",\")}`;\\n}\\n\\nif (remote.length != \"\") {\\n // Transform remote options to LinkedIn codes\\n // On-Site -> 1, Remote -> 2, Hybrid -> 3\\n const transformedRemote = remote\\n .split(\",\")\\n .map((e) => {\\n switch (e.trim()) {\\n case \"Remote\": return \"2\";\\n case \"Hybrid\": return \"3\";\\n case \"On-Site\": return \"1\";\\n default: return \"\";\\n }\\n })\\n .filter(Boolean);\\n url += `&f_WT=${transformedRemote.join(\",\")}`;\\n}\\n\\n\\n\\n\\nreturn {url}\\n"
},
"typeVersion": 2
},
{
"id": "b109d1ed-191b-4d6d-a1a9-40add6168f5b",
"name": "LinkedIn Search URL7",
"type": "n8n-nodes-base.code",
"position": [
-1616,
2128
],
"parameters": {
"jsCode": "let url = \"https://www.linkedin.com/jobs/search/?f_TPR=r86400\"\\n\\nconst keyword = $input.first().json.output.alternatekeyword2\\nconst location = $input.first().json.output.location\\nconst experienceLevel = $input.first().json.output.experiencelevel\\nconst remote = $input.first().json.output.remote\\n\\n\\n\\nif (keyword != \"\") {\\n url += `&keywords=${keyword}`;\\n}\\n\\nif (location != \"\") {\\n url += `&location=${location}`;\\n}\\n\\nif (experienceLevel !== \"\") {\\n // Transform experience levels to LinkedIn codes\\n // Internship -> 1, Entry level -> 2, Associate -> 3\\n // Mid-Senior level -> 4, Director -> 5, Executive -> 6\\n const transformedExperiences = experienceLevel\\n .split(\",\")\\n .map((exp) => {\\n switch (exp.trim()) {\\n case \"Director\": return \"5\";\\n case \"Mid-Senior Level\": return \"4\";\\n case \"Internship\": return \"1\";\\n case \"New Grad\": return \"3\";\\n default: return \"\";\\n }\\n })\\n .filter(Boolean);\\n url += `&f_E=${transformedExperiences.join(\",\")}`;\\n}\\n\\nif (remote.length != \"\") {\\n // Transform remote options to LinkedIn codes\\n // On-Site -> 1, Remote -> 2, Hybrid -> 3\\n const transformedRemote = remote\\n .split(\",\")\\n .map((e) => {\\n switch (e.trim()) {\\n case \"Remote\": return \"2\";\\n case \"Hybrid\": return \"3\";\\n case \"On-Site\": return \"1\";\\n default: return \"\";\\n }\\n })\\n .filter(Boolean);\\n url += `&f_WT=${transformedRemote.join(\",\")}`;\\n}\\n\\n\\n\\n\\nreturn {url}\\n"
},
"typeVersion": 2
},
{
"id": "e11488d7-d8c2-4924-a092-b67db957ecee",
"name": "Fetch jobs from LinkedIn",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1440,
1696
],
"parameters": {
"url": "={{ $json.url }}",
"options": {}
},
"retryOnFail": true,
"typeVersion": 4.2,
"waitBetweenTries": 3000
},
{
"id": "f4e30a7b-74ed-426d-a73c-9edef904edee",
"name": "Fetch jobs from LinkedIn3",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1440,
1840
],
"parameters": {
"url": "={{ $json.url }}",
"options": {}
},
"retryOnFail": true,
"typeVersion": 4.2,
"waitBetweenTries": 3000
},
{
"id": "a0af0c7d-d6e5-47bc-a92b-dc8ae23736fc",
"name": "Fetch jobs from Linkedin",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1440,
1984
],
"parameters": {
"url": "={{ $json.url }}",
"options": {}
},
"retryOnFail": true,
"typeVersion": 4.2,
"waitBetweenTries": 3000
},
{
"id": "1b86a6e1-e74b-45e5-a044-7061a4217c53",
"name": "Fetch jobs from LinkedIn5",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1440,
2128
],
"parameters": {
"url": "={{ $json.url }}",
"options": {}
},
"retryOnFail": true,
"typeVersion": 4.2,
"waitBetweenTries": 3000
},
{
"id": "1e4498be-6780-4dd8-8229-1916a31b6779",
"name": "Attendre1",
"type": "n8n-nodes-base.wait",
"position": [
-1056,
2192
],
"webhookId": "1f33d10b-6071-480b-865b-835b93a7841d",
"parameters": {
"amount": 3
},
"typeVersion": 1.1
},
{
"id": "cb0aa113-f1ec-42ad-b0da-afc039c7ba27",
"name": "Requête HTTP1",
"type": "n8n-nodes-base.httpRequest",
"position": [
-880,
2192
],
"parameters": {
"url": "={{ $json.jobs }}",
"options": {}
},
"retryOnFail": true,
"typeVersion": 4.2,
"waitBetweenTries": 5
},
{
"id": "c61f2149-3375-44eb-8d2c-4abfcec7ecea",
"name": "HTML3",
"type": "n8n-nodes-base.html",
"position": [
-1056,
1920
],
"parameters": {
"options": {},
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "jobs",
"attribute": "href",
"cssSelector": "ul.jobs-search__results-list li div a[class*=\"base-card\"]",
"returnArray": true,
"returnValue": "attribute"
}
]
}
},
"retryOnFail": true,
"typeVersion": 1.2
},
{
"id": "ba3ae6dd-5525-46e6-8afd-64f370a6707c",
"name": "Boucler sur les éléments3",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-1232,
2144
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "c60ca1ba-ce73-4769-a71b-46729ed0ec68",
"name": "Edit Fields2",
"type": "n8n-nodes-base.set",
"position": [
-880,
2368
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "240418dc-3349-48d5-ba59-3aa590d71950",
"name": "Description",
"type": "string",
"value": "={{ $json.Description.replaceAll(/\\s+/g, \" \")}}"
},
{
"id": "7b24938f-8b47-488d-9f65-54d036dcffd5",
"name": "Job ID",
"type": "string",
"value": "={{ $json['Job ID'].split(\":\").last() }}"
},
{
"id": "d6addada-9e01-464f-a768-c19f6224c491",
"name": "Apply Link",
"type": "string",
"value": "={{ \"https://www.linkedin.com/jobs/view/\"+ $json['Job ID'].split(\":\").last() }}"
},
{
"id": "21c89d3a-c8b6-44eb-9719-9ae5716a7c76",
"name": "Title",
"type": "string",
"value": "={{ $json.Title }}"
},
{
"id": "da713845-9a81-486e-bff2-1613105e424d",
"name": "Company",
"type": "string",
"value": "={{ $json.Company }}"
},
{
"id": "1a9a31dd-8d51-4f35-b6b6-ca8a348de5d7",
"name": "Location",
"type": "string",
"value": "={{ $json.Location }}"
},
{
"id": "f5b6b114-3896-4fef-b63c-202bf0e194d6",
"name": "Salary",
"type": "string",
"value": "={{ $json.Salary }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "627c9f8a-0f9c-4583-b15f-6fbd63fa99e1",
"name": "Edit Fields3",
"type": "n8n-nodes-base.set",
"position": [
96,
2208
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "={{ (() => {\\n // 0) Read model output from AI Agent node\\n const raw = String(($('Job Matching AI Agent1').item.json.output ?? ''));\\n\\n // 1) Strip code fences\\n let s = raw\\n .replace(/`{2,3}(?:json)?\\s*/gi, '')\\n .replace(/\\s*`{3}\\s*$/gi, '');\\n\\n // 1b) Normalize curly quotes (Gemini sometimes emits them)\\n s = s.replace(/[\\u201C\\u201D]/g, '\"').replace(/\\u2019/g, \"'\");\\n\\n // 2) Cut at END_OF_JSON if present\\n const endIdx = s.indexOf('END_OF_JSON');\\n if (endIdx !== -1) s = s.slice(0, endIdx);\\n\\n // 3) Extract first complete { ... } by brace counting\\n const start = s.indexOf('{');\\n if (start < 0) return {};\\n let depth = 0, inStr = false, esc = false, end = -1;\\n for (let i = start; i < s.length; i++) {\\n const ch = s[i];\\n if (inStr) {\\n if (esc) { esc = false; }\\n else if (ch === '\\\\') { esc = true; }\\n else if (ch === '\"') { inStr = false; }\\n } else {\\n if (ch === '\"') inStr = true;\\n else if (ch === '{') depth++;\\n else if (ch === '}') { depth--; if (depth === 0) { end = i + 1; break; } }\\n }\\n }\\n if (end < 0) return {};\\n\\n const cleaned = s.slice(start, end).trim();\\n\\n try {\\n return JSON.parse(cleaned); // JSON mode expects an OBJECT\\n } catch (e) {\\n // As a last resort, return an empty object to keep the run alive\\n return {};\\n }\\n})() }\\n"
},
"typeVersion": 3.4
},
{
"id": "9b639e1e-8eef-43fb-a457-dbd5cdbef864",
"name": "Get row(s) in sheet1",
"type": "n8n-nodes-base.googleSheets",
"position": [
-192,
2000
],
"parameters": {
"options": {},
"sheetName": "[YOUR_SHEET_NAME]",
"documentId": "[YOUR_SPREADSHEET_ID]"
},
"typeVersion": 4.7
},
{
"id": "1f464b53-ccf2-45ad-8990-6c801912cebc",
"name": "Trier1",
"type": "n8n-nodes-base.sort",
"position": [
-16,
2000
],
"parameters": {
"options": {},
"sortFieldsUi": {
"sortField": [
{
"order": "descending",
"fieldName": "Score"
}
]
}
},
"typeVersion": 1
},
{
"id": "1e005421-67ec-4144-a705-1149443a73af",
"name": "Limiter1",
"type": "n8n-nodes-base.limit",
"position": [
160,
2000
],
"parameters": {
"maxItems": 5
},
"typeVersion": 1
},
{
"id": "02e430ca-d7d5-4ae2-ae72-99c51a613962",
"name": "Agréger1",
"type": "n8n-nodes-base.aggregate",
"position": [
336,
2000
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "201a41c3-5476-4b63-875e-34a245561231",
"name": "Note adhésive11",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1824,
1424
],
"parameters": {
"color": 5,
"width": 192,
"height": 144,
"content": "## Update\n\nProvide your resume by updating the URL in the Download Resume node."
},
"typeVersion": 1
},
{
"id": "c6c9dce9-3c79-4945-812e-4e5bcfad1437",
"name": "Note adhésive12",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1008,
1424
],
"parameters": {
"color": 5,
"width": 176,
"height": 144,
"content": "## Update\n\nLink Google Sheets to your Job Search data base."
},
"typeVersion": 1
},
{
"id": "84e3a6d4-21a6-4781-8961-53c162eb9084",
"name": "Note adhésive13",
"type": "n8n-nodes-base.stickyNote",
"position": [
288,
2432
],
"parameters": {
"color": 5,
"width": 176,
"height": 144,
"content": "## Create\n\nCreate a Google Sheet with the columns listed in this node."
},
"typeVersion": 1
},
{
"id": "e330aea3-2d21-4647-a691-807e24aff418",
"name": "Note adhésive14",
"type": "n8n-nodes-base.stickyNote",
"position": [
512,
1776
],
"parameters": {
"color": 5,
"width": 272,
"height": 192,
"content": "## Update\nGive your email a personal touch. Change colors, font, spacing, and more. \n\nTip: If you are not good with HTML, give the HTML in this node to AI and let it format for you."
},
"typeVersion": 1
},
{
"id": "2417ee79-3ee9-4d9c-ac44-f10911b26148",
"name": "Note adhésive15",
"type": "n8n-nodes-base.stickyNote",
"position": [
-48,
1904
],
"parameters": {
"color": 4,
"width": 320,
"height": 80,
"content": "Only focussing on the top 5 results. If you want to see more results per email, change the Limit node to your desired output."
},
"typeVersion": 1
},
{
"id": "9f744607-6ba2-49a8-9ee4-14d14cffb8ac",
"name": "Note adhésive16",
"type": "n8n-nodes-base.stickyNote",
"position": [
-224,
1984
],
"parameters": {
"color": 7,
"width": 976,
"content": ""
},
"typeVersion": 1
},
{
"id": "68df2a48-8d51-45f8-8d92-baea1f928d88",
"name": "Note adhésive17",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1648,
1632
],
"parameters": {
"color": 7,
"width": 336,
"height": 672,
"content": "## Job Scraping"
},
"typeVersion": 1
},
{
"id": "c9f349a1-cfa2-4884-a7da-4d3cd7bfc92a",
"name": "Note adhésive18",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1648,
2320
],
"parameters": {
"color": 4,
"width": 320,
"height": 176,
"content": "Job scraping is acheived using the 4 key job titles that match your resume. \\n\\nTip: For the first run it might be beneficial to search all jobs. Remove \"r86400\" to list all jobs available. Add \"r86400\" back in after the first successful run to only search past 24 hours."
},
"typeVersion": 1
},
{
"id": "f749941b-3679-4fe3-9975-0f51a2138f31",
"name": "Note adhésive19",
"type": "n8n-nodes-base.stickyNote",
"position": [
-656,
1584
],
"parameters": {
"width": 400,
"height": 320,
"content": "# Description\n\nThis advanced workflow automates the entire job search and preparation process, moving beyond simple notifications to provide AI-driven career intelligence.\n\nIt connects to LinkedIn to scrape fresh job postings, filters against jobs you’ve already seen, and then uses powerful LLMs (Mistral Large/Small) to perform a detailed resume-to-job match, generate tailored cover letters, and provide concrete resume improvement suggestions. All data is logged into a Google Sheet for comprehensive tracking, and a clean, single Daily Digest Email summarizes the top 5 matches found each day."
},
"typeVersion": 1
},
{
"id": "0d8031cf-bda3-4e70-a717-9bc56634e91c",
"name": "Note adhésive20",
"type": "n8n-nodes-base.stickyNote",
"position": [
-240,
2176
],
"parameters": {
"color": 7,
"width": 512,
"height": 608,
"content": ""
},
"typeVersion": 1
},
{
"id": "178064f1-34fb-4668-a92a-2b2dadbd42ae",
"name": "Note adhésive21",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
2640
],
"parameters": {
"color": 4,
"width": 208,
"height": 80,
"content": "Want a personal touch? Review the AI prompts to give personal guidance. "
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"6716f4ea-2529-4665-bdfa-8943a8cd5ffc": {
"main": [
[
{
"node": "c60ca1ba-ce73-4769-a71b-46729ed0ec68",
"type": "main",
"index": 0
}
]
]
},
"c61f2149-3375-44eb-8d2c-4abfcec7ecea": {
"main": [
[
{
"node": "01e0b4e5-4b4d-4102-8a67-a1c0ab90717a",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "c61f2149-3375-44eb-8d2c-4abfcec7ecea",
"type": "main",
"index": 0
}
]
]
},
"Sort1": {
"main": [
[
{
"node": "Limit1",
"type": "main",
"index": 0
}
]
]
},
"Wait1": {
"main": [
[
{
"node": "HTTP Request1",
"type": "main",
"index": 0
}
]
]
},
"Limit1": {
"main": [
[
{
"node": "Aggregate1",
"type": "main",
"index": 0
}
]
]
},
"01e0b4e5-4b4d-4102-8a67-a1c0ab90717a": {
"main": [
[
{
"node": "Loop Over Items3",
"type": "main",
"index": 0
}
]
]
},
"Aggregate1": {
"main": [
[
{
"node": "244d669c-b275-47ce-ade8-bf33dd5761f5",
"type": "main",
"index": 0
}
]
]
},
"c60ca1ba-ce73-4769-a71b-46729ed0ec68": {
"main": [
[
{
"node": "Loop Over Items3",
"type": "main",
"index": 0
}
]
]
},
"627c9f8a-0f9c-4583-b15f-6fbd63fa99e1": {
"main": [
[
{
"node": "Resume Analysis AI Agent1",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request1": {
"main": [
[
{
"node": "6716f4ea-2529-4665-bdfa-8943a8cd5ffc",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[
{
"node": "9b639e1e-8eef-43fb-a457-dbd5cdbef864",
"type": "main",
"index": 0
}
],
[
{
"node": "Job Matching AI Agent1",
"type": "main",
"index": 0
}
]
]
},
"571dd21c-635a-4198-9013-37670859bcf0": {
"main": [
[
{
"node": "fe0bc527-e772-4c99-9566-aef4cadefc55",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items3": {
"main": [
[
{
"node": "65c5c979-8b77-47c5-856b-68a6d8e24203",
"type": "main",
"index": 1
}
],
[
{
"node": "Wait1",
"type": "main",
"index": 0
}
]
]
},
"65c5c979-8b77-47c5-856b-68a6d8e24203": {
"main": [
[],
[],
[],
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"65b3a2cd-d78b-4f44-9bd1-16be870bf431": {
"main": [
[
{
"node": "b109d1ed-191b-4d6d-a1a9-40add6168f5b",
"type": "main",
"index": 0
},
{
"node": "a80f67f7-8dde-45f7-ba11-74db416877ea",
"type": "main",
"index": 0
},
{
"node": "361256c2-5e01-4195-bc02-c8d7071a9356",
"type": "main",
"index": 0
},
{
"node": "454faa82-eee8-4faa-b9e9-c6832452fbc6",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger1": {
"main": [
[
{
"node": "571dd21c-635a-4198-9013-37670859bcf0",
"type": "main",
"index": 0
},
{
"node": "5f720ba5-cccd-4428-a948-d74dbd02e9ba",
"type": "main",
"index": 0
}
]
]
},
"a80f67f7-8dde-45f7-ba11-74db416877ea": {
"main": [
[
{
"node": "e11488d7-d8c2-4924-a092-b67db957ecee",
"type": "main",
"index": 0
}
]
]
},
"fe0bc527-e772-4c99-9566-aef4cadefc55": {
"main": [
[
{
"node": "65b3a2cd-d78b-4f44-9bd1-16be870bf431",
"type": "main",
"index": 0
}
]
]
},
"9b639e1e-8eef-43fb-a457-dbd5cdbef864": {
"main": [
[
{
"node": "Sort1",
"type": "main",
"index": 0
}
]
]
},
"361256c2-5e01-4195-bc02-c8d7071a9356": {
"main": [
[
{
"node": "f4e30a7b-74ed-426d-a73c-9edef904edee",
"type": "main",
"index": 0
}
]
]
},
"454faa82-eee8-4faa-b9e9-c6832452fbc6": {
"main": [
[
{
"node": "a0af0c7d-d6e5-47bc-a92b-dc8ae23736fc",
"type": "main",
"index": 0
}
]
]
},
"b109d1ed-191b-4d6d-a1a9-40add6168f5b": {
"main": [
[
{
"node": "1b86a6e1-e74b-45e5-a044-7061a4217c53",
"type": "main",
"index": 0
}
]
]
},
"Job Matching AI Agent1": {
"main": [
[
{
"node": "627c9f8a-0f9c-4583-b15f-6fbd63fa99e1",
"type": "main",
"index": 0
}
]
]
},
"e11488d7-d8c2-4924-a092-b67db957ecee": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"a0af0c7d-d6e5-47bc-a92b-dc8ae23736fc": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 2
}
]
]
},
"e08473bb-0b75-4ceb-81c0-92be71165fb4": {
"ai_languageModel": [
[
{
"node": "65b3a2cd-d78b-4f44-9bd1-16be870bf431",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"e1a36beb-28ad-40c6-b534-28c9e84a5b9c": {
"ai_outputParser": [
[
{
"node": "65b3a2cd-d78b-4f44-9bd1-16be870bf431",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"f4e30a7b-74ed-426d-a73c-9edef904edee": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"1b86a6e1-e74b-45e5-a044-7061a4217c53": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 3
}
]
]
},
"5f720ba5-cccd-4428-a948-d74dbd02e9ba": {
"main": [
[
{
"node": "65c5c979-8b77-47c5-856b-68a6d8e24203",
"type": "main",
"index": 0
}
]
]
},
"92eda0a2-50f8-43e8-946b-52014ad79eeb": {
"ai_languageModel": [
[
{
"node": "Resume Analysis AI Agent1",
"type": "ai_languageModel",
"index": 0
},
{
"node": "Job Matching AI Agent1",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Resume Analysis AI Agent1": {
"main": [
[
{
"node": "6d15572d-5eb3-4959-b8ae-df09c4746d27",
"type": "main",
"index": 0
}
]
]
},
"6d15572d-5eb3-4959-b8ae-df09c4746d27": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
}
}
}Comment utiliser ce workflow ?
Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.
Dans quelles scénarios ce workflow est-il adapté ?
Avancé - Productivité personnelle, Résumé IA
Est-ce payant ?
Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.
Workflows recommandés
Jordan Hoyle
@jordanhoylePartager ce workflow