Automate Your Entire Job Search With Custom Resume Generator with AI (LinkedIn + n8n)

    Shared 12/1/2025

    62 views

    Visual Workflow

    JSON Code

    {
      "id": "olCgxDQqnZ4VYFJ9",
      "meta": {
        "instanceId": "9fb634bb746cbe3fe9f7fa86fdd635510d2f0c24c4ba919fbba1ca6aec3c4e57",
        "templateCredsSetupCompleted": true
      },
      "name": "LinkedIn Job Search + Resume Builder Agent",
      "tags": [],
      "nodes": [
        {
          "id": "ff08db49-bf0f-4e13-a1d2-ef55635b0e4b",
          "name": "Error Trigger",
          "type": "n8n-nodes-base.errorTrigger",
          "position": [
            3504,
            -144
          ],
          "parameters": {},
          "typeVersion": 1
        },
        {
          "id": "5d847601-8a11-4846-8249-dd3614d46a3b",
          "name": "Sticky Note2",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            3392,
            -336
          ],
          "parameters": {
            "color": 3,
            "width": 688,
            "height": 480,
            "content": "## 🚨 Error and Loop Control Flow\n---"
          },
          "typeVersion": 1
        },
        {
          "id": "f75ada58-7ca3-492d-9aca-c8679b2e9113",
          "name": "Send Error Email",
          "type": "n8n-nodes-base.mailjet",
          "position": [
            3776,
            -144
          ],
          "parameters": {
            "text": "=Error: {{ $json.execution.error.message }}\nPlease check workflow {{ $json.execution.url }}\nLast Node Exe.: {{ $json.execution.lastNodeExecuted }}",
            "subject": "Error In Linked Job Search With Resume",
            "toEmail": "={{ $('Set Global Vars').first().json.to_email }}",
            "fromEmail": "={{ $('Set Global Vars').first().json.from_email }}",
            "additionalFields": {}
          },
          "credentials": {
            "mailjetEmailApi": {
              "id": "goG8DkfduUdx12gE",
              "name": "Mailjet Email account"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "adda0e1e-9377-4d4f-82e9-c7ead2f78589",
          "name": "Sticky Note12",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            2816,
            208
          ],
          "parameters": {
            "width": 528,
            "height": 1040,
            "content": "# 🛠️ Setup Guide  \n\n**Author: [Sohail Jafri](https://link.thesohailjafri.me/web_ai) | [Youtube](https://link.thesohailjafri.me/yt_ai) | [Instagram](https://link.thesohailjafri.me/ig_ai) | [Join Skool](https://link.thesohailjafri.me/join-skool)**\n\nFollow the steps below to get your LinkedIn Job Scraper running:\n\n### ✅ Step 1: Copy the Google Sheet template  \nMake a copy of the sheet and connect it to the **[Append row in sheet](https://docs.google.com/spreadsheets/d/1awPFjM8r-fimvwiYznWLMF1KxRx2MRDrqs-fYWnaOs4/copy)** node.  \nTemplate: \n\n### ✅ Step 2: Add [Apify Data](https://console.apify.com/) credentials  \nAdd your Apify Data token to the Apify nodes.  \n\n### ✅ Step 3: Connect your AI models  \nAdd your OpenRouter (or preferred LLM) API key to both LM nodes used for screening and email composition.  \n- [OpenRouter](https://openrouter.ai/)\n- [OpenAI](https://platform.openai.com/)\n\n### ✅ Step 4: Add [Resend](https://resend.com/) API key  \nConfigure the Resend bearer token in the **Send Mail** node to enable daily emails.  \n\n\n### ✅ Step 5: Populate Edit Fields  \nFill these values in the **Edit Fields** node:  \n- **ABOUT_ME** — short resume summary  \n- **MY_SKILL** — comma-separated skill list  \n- **JOB_INPUT** — array of search queries (location, keyword, time_range, job_type, experience_level, remote)\n\n### ✅ Step 6: Test the flow  \nRun the workflow manually with a small JOB_INPUT to confirm end-to-end success.\n\n### 💡 Quick config tips\n- **Poll interval** = 2 minutes (adjust in **Wait - Polling Apify Data** if needed).  \n- Ensure the Google Sheet columns match the node schema.  \n- Verify the email **to** address in **Send Mail** before enabling production.\n\n### ✅ Final step: Enable workflow  \nSwitch the workflow to active after confirming credentials and a successful test run.\n "
          },
          "typeVersion": 1
        },
        {
          "id": "2b99ec13-6286-4ec6-84f1-db8687916237",
          "name": "Schedule Trigger",
          "type": "n8n-nodes-base.scheduleTrigger",
          "position": [
            3472,
            416
          ],
          "parameters": {
            "rule": {
              "interval": [
                {
                  "triggerAtHour": 7
                }
              ]
            }
          },
          "typeVersion": 1.2
        },
        {
          "id": "7e77f910-e1b2-4905-88ef-c2e240b12aa9",
          "name": "Get a document",
          "type": "n8n-nodes-base.googleDocs",
          "position": [
            3472,
            640
          ],
          "parameters": {
            "operation": "get",
            "documentURL": "https://docs.google.com/document/d/1RNn2GSQ25VNJ585dPNvnycd6NwdQJ41EpbpLpUBl9ZI/edit?usp=sharing"
          },
          "credentials": {
            "googleDocsOAuth2Api": {
              "id": "lLgokgGVQ6UHx1G3",
              "name": "Google Docs account"
            }
          },
          "typeVersion": 2
        },
        {
          "id": "49e18951-4366-4547-b699-f4ac83f4b285",
          "name": "Array Of Job Inputs",
          "type": "n8n-nodes-base.code",
          "position": [
            3872,
            640
          ],
          "parameters": {
            "jsCode": "const inputs = [\n  {\n    \"experience_level\": \"4\",\n    \"job_post_time\": \"r86400\",\n    \"job_title\": \"automation engineer\",\n    \"job_type\": \"F\",\n    \"jobs_entries\": 5,\n    \"location\": \"United States\",\n    \"start_jobs\": 0,\n    \"work_schedule\": \"2\"\n  },\n   {\n    \"experience_level\": \"4\",\n    \"job_post_time\": \"r86400\",\n    \"job_title\": \"ai engineer\",\n    \"job_type\": \"F\",\n    \"jobs_entries\": 5,\n    \"location\": \"United States\",\n    \"start_jobs\": 0,\n    \"work_schedule\": \"2\"\n  }\n]\n\nreturn inputs.map(i=> ({ json: i }))\n"
          },
          "typeVersion": 2
        },
        {
          "id": "f5a32e52-7d43-416d-9224-e26958adc891",
          "name": "Format Input",
          "type": "n8n-nodes-base.code",
          "position": [
            4368,
            432
          ],
          "parameters": {
            "jsCode": "let input = JSON.stringify($input.first().json)\nreturn {\n  json: {\n    input\n  }\n}"
          },
          "typeVersion": 2
        },
        {
          "id": "7a2f7aa6-bf98-4432-92a6-a5fe97b18b23",
          "name": "Run an Actor",
          "type": "@apify/n8n-nodes-apify.apify",
          "position": [
            4576,
            432
          ],
          "parameters": {
            "actorId": {
              "__rl": true,
              "mode": "list",
              "value": "JkfTWxtpgfvcRQn3p",
              "cachedResultUrl": "https://console.apify.com/actors/JkfTWxtpgfvcRQn3p/input",
              "cachedResultName": "⚡️Rapid Linkedin Jobs Scraper (No Cookies) | Free Jobs Scraper (worldunboxer/rapid-linkedin-scraper)"
            },
            "timeout": {},
            "customBody": "={{ $json.input }}"
          },
          "credentials": {
            "apifyApi": {
              "id": "QYF5ZYibqkW9a0UB",
              "name": "Apify account"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "1bb1ebfc-8d5a-4f66-936f-2ecd9f457267",
          "name": "Wait",
          "type": "n8n-nodes-base.wait",
          "position": [
            4816,
            432
          ],
          "webhookId": "9450a98c-ded2-40c6-b2cd-22fa1679bc1e",
          "parameters": {
            "unit": "minutes",
            "amount": 1
          },
          "typeVersion": 1.1
        },
        {
          "id": "0e0abd2c-9e7e-4ca9-a67f-551980bd02f0",
          "name": "Get run",
          "type": "@apify/n8n-nodes-apify.apify",
          "position": [
            5008,
            432
          ],
          "parameters": {
            "runId": {
              "__rl": true,
              "mode": "id",
              "value": "={{ $json.id }}"
            },
            "resource": "Actor runs",
            "operation": "Get run"
          },
          "credentials": {
            "apifyApi": {
              "id": "QYF5ZYibqkW9a0UB",
              "name": "Apify account"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "799be29d-4666-47a7-b02f-f617cacab16c",
          "name": "If SUCCEEDED",
          "type": "n8n-nodes-base.if",
          "position": [
            5408,
            448
          ],
          "parameters": {
            "options": {},
            "conditions": {
              "options": {
                "version": 2,
                "leftValue": "",
                "caseSensitive": true,
                "typeValidation": "strict"
              },
              "combinator": "and",
              "conditions": [
                {
                  "id": "c5caa229-b0db-42d9-bc94-c97ee11cc9a7",
                  "operator": {
                    "name": "filter.operator.equals",
                    "type": "string",
                    "operation": "equals"
                  },
                  "leftValue": "={{ $json.status }}",
                  "rightValue": "SUCCEEDED"
                }
              ]
            }
          },
          "typeVersion": 2.2
        },
        {
          "id": "68edb611-8bff-45f9-bc79-971c3e6f987d",
          "name": "If RUNNING",
          "type": "n8n-nodes-base.if",
          "position": [
            5200,
            432
          ],
          "parameters": {
            "options": {},
            "conditions": {
              "options": {
                "version": 2,
                "leftValue": "",
                "caseSensitive": true,
                "typeValidation": "strict"
              },
              "combinator": "and",
              "conditions": [
                {
                  "id": "a6e65b5b-2e60-458d-91ad-ac90aea7943d",
                  "operator": {
                    "type": "string",
                    "operation": "equals"
                  },
                  "leftValue": "={{ $json.status }}",
                  "rightValue": "RUNNING"
                }
              ]
            }
          },
          "typeVersion": 2.2
        },
        {
          "id": "3e930311-fa14-4bbd-9138-7cbdb389e1cc",
          "name": "Stop and Error",
          "type": "n8n-nodes-base.stopAndError",
          "position": [
            5616,
            464
          ],
          "parameters": {
            "errorMessage": "Apify Status is not SUCCEEDED/RUNNING"
          },
          "typeVersion": 1
        },
        {
          "id": "e21a162c-64f5-442c-85aa-b8f1147a643f",
          "name": "Get dataset items",
          "type": "@apify/n8n-nodes-apify.apify",
          "position": [
            5632,
            656
          ],
          "parameters": {
            "offset": {},
            "resource": "Datasets",
            "datasetId": "={{ $json.defaultDatasetId }}"
          },
          "credentials": {
            "apifyApi": {
              "id": "QYF5ZYibqkW9a0UB",
              "name": "Apify account"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "f74a860b-2358-4734-ac70-411f6cbe53b5",
          "name": "Remove Duplicate Jobs",
          "type": "n8n-nodes-base.code",
          "position": [
            4496,
            624
          ],
          "parameters": {
            "jsCode": "const jobs = $input.all().map(i=>i.json)\nconst seen = new Set();\nconst uniqueJobs = [];\n\nfor (const job of jobs) {\n  if (!seen.has(job.job_id)) {\n    seen.add(job.job_id);\n    uniqueJobs.push({ json:job });\n  }\n}\n\nreturn uniqueJobs\n"
          },
          "typeVersion": 2
        },
        {
          "id": "6c9f44e6-04ce-4d6d-9e71-6f900cc6367b",
          "name": "Set Global Vars",
          "type": "n8n-nodes-base.set",
          "position": [
            3680,
            640
          ],
          "parameters": {
            "options": {},
            "assignments": {
              "assignments": [
                {
                  "id": "e4ce6e58-d537-42d3-9566-5555fbcd1ab3",
                  "name": "resume",
                  "type": "string",
                  "value": "={{ $json.content }}"
                },
                {
                  "id": "d7c207ae-5f67-4de7-a1bd-5d8d7706ae5b",
                  "name": "telegram_chat_id",
                  "type": "string",
                  "value": "your_val"
                },
                {
                  "id": "a5ff23c2-635f-4f1a-bb87-78df4d199949",
                  "name": "from_email",
                  "type": "string",
                  "value": "your_val"
                },
                {
                  "id": "3d304c86-c2f1-49f1-a90a-e581e5b8d4a6",
                  "name": "to_email",
                  "type": "string",
                  "value": "your_val"
                }
              ]
            }
          },
          "typeVersion": 3.4
        },
        {
          "id": "bbf151cb-2bdd-4075-9d80-0b7179acd659",
          "name": "Job Match AI",
          "type": "@n8n/n8n-nodes-langchain.agent",
          "position": [
            3888,
            1328
          ],
          "parameters": {
            "text": "=Here is the job description and my current resume. Evaluate alignment and generate output as defined in the System Prompt.\n\n### Job Description:\n{{ $json.job_description }}\n\n### My Resume:\n{{ $('Set Global Vars').item.json.resume }}",
            "options": {
              "systemMessage": "You are an expert ATS evaluator and résumé matching assistant.\n\nYour job is to analyze a Job Description (JD) and compare it with the user’s résumé.\n\nYour output must be a JSON object with the following structure:\n{\n  \"relevancy\": boolean,\n  \"score\": \"XX%\",\n  \"justification\": [\n    \"Short bullet reasoning 1\",\n    \"Short bullet reasoning 2\",\n    \"Short bullet reasoning 3\"\n  ]\n}\n\nScoring rules:\n- Identify key JD requirements (skills, technologies, responsibilities, seniority)\n- Compare them against the résumé\n- Score must be an integer % between \"0%\" and \"100%\"\n- relevancy = true if score ≥ 50%, else false\n\nRules:\n- Justification must be 3–7 short bullets\n- JSON only — no text outside JSON\n- No hallucinations: base everything strictly on the provided résumé and JD"
            },
            "promptType": "define",
            "hasOutputParser": true
          },
          "typeVersion": 2.2
        },
        {
          "id": "9e6c2fda-a3ae-4bb9-8328-0bc7b6bdd70d",
          "name": "Structured Output Parser",
          "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
          "position": [
            4096,
            1552
          ],
          "parameters": {
            "schemaType": "manual",
            "inputSchema": "{\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"title\": \"Job Match & Tailored Resume Output\",\n  \"type\": \"object\",\n  \"required\": [\"relevancy\", \"score\", \"justification\"],\n  \"properties\": {\n    \"relevancy\": {\n      \"type\": \"boolean\",\n      \"description\": \"True if candidate suitability score >= 50%, else False\"\n    },\n    \"score\": {\n      \"type\": \"string\",\n      \"pattern\": \"^(100|[1-9][0-9]?|0)%$\",\n      \"description\": \"Percentage string from 0% to 100% without leading zeros\"\n    },\n    \"justification\": {\n      \"type\": \"array\",\n      \"minItems\": 1,\n      \"maxItems\": 10,\n      \"items\": {\n        \"type\": \"string\",\n        \"minLength\": 5,\n        \"description\": \"Short bullet point reasoning for relevancy\"\n      }\n    }\n  },\n  \"additionalProperties\": false\n}\n"
          },
          "typeVersion": 1.3
        },
        {
          "id": "8f1bf62a-125c-47d7-b4a8-a9320ebc965a",
          "name": "OpenRouter Chat Model",
          "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
          "position": [
            3888,
            1552
          ],
          "parameters": {
            "options": {}
          },
          "credentials": {
            "openRouterApi": {
              "id": "6z6zYFNsqyqFRUEB",
              "name": "OpenRouter account"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "de3ee6dc-0bf3-4e89-806b-bd3216893ad8",
          "name": "If Relevant",
          "type": "n8n-nodes-base.if",
          "position": [
            4240,
            1328
          ],
          "parameters": {
            "options": {},
            "conditions": {
              "options": {
                "version": 2,
                "leftValue": "",
                "caseSensitive": true,
                "typeValidation": "strict"
              },
              "combinator": "and",
              "conditions": [
                {
                  "id": "978abce9-0189-45fe-9e28-ba74633b4561",
                  "operator": {
                    "type": "boolean",
                    "operation": "true",
                    "singleValue": true
                  },
                  "leftValue": "={{ $json.output.relevancy }}",
                  "rightValue": ""
                }
              ]
            }
          },
          "typeVersion": 2.2
        },
        {
          "id": "3e9bcfa7-40c4-4124-9d2f-2e487a142f22",
          "name": "Resume Tailoring Ai",
          "type": "@n8n/n8n-nodes-langchain.agent",
          "position": [
            4448,
            1312
          ],
          "parameters": {
            "text": "=Here is the job description and my current resume. Evaluate alignment and generate output as defined in the System Prompt.\n\n### Job Description:\n{{ $('Set Job Details').item.json.job_description }}\n\n### My Resume:\n{{ $('Set Global Vars').item.json.resume }}",
            "options": {
              "systemMessage": "You are an expert ATS résumé optimizer.\nYour goal: rewrite the provided resume into a structured, clean HTML resume tailored to the given Job Description (JD).\n\nRules:\n- Only semantic HTML tags: <h1>, <h2>, <section>, <article>, <ul>, <li>, <p>, <strong>, <em>, <time>\n- No CSS, no inline style, no class or id attributes\n- Must strictly follow the exact section structure below:\n\n<h1>{FULL NAME}</h1>\n\n<h2>Contact</h2>\n<ul>\n  <li>Email: {EMAIL}</li>\n  <li>Location: {CITY, COUNTRY}</li>\n  <li>GitHub: {URL or Not provided.}</li>\n  <li>Portfolio: {URL or Not provided.}</li>\n  <li>LinkedIn: {URL or Not provided.}</li>\n</ul>\n\n<h2>Title</h2>\n<p>{ROLE TITLE tailored to JD}</p>\n\n<h2>Summary</h2>\n<ul>\n  <li>3 bullet achievements aligned to JD</li>\n</ul>\n\n<h2>Technical Skills</h2>\n<ul>\n  <li><strong>Programming Languages:</strong> …</li>\n  <li><strong>Frameworks &amp; Libraries:</strong> …</li>\n  <li><strong>Cloud &amp; DevOps:</strong> …</li>\n  <li><strong>Databases:</strong> …</li>\n  <li><strong>Tools &amp; Others:</strong> …</li>\n</ul>\n\n<h2>Experience</h2>\n<section>\n  <!-- 1+ article blocks based on provided resume -->\n</section>\n\n<h2>Projects</h2>\n<section>\n  <!-- 1+ article blocks; if none, include placeholder -->\n</section>\n\n<h2>Education</h2>\n<ul>\n  <li>{Degree} OR <em>Not provided.</em></li>\n</ul>\n\n<h2>Certifications</h2>\n<ul>\n  <li>{Certification} OR <em>Not provided.</em></li>\n</ul>\n\n<h2>Achievements</h2>\n<ul>\n  <li>{Achievement} OR <em>Not provided.</em></li>\n</ul>\n\n<h2>Additional Information</h2>\n<ul>\n  <li>Languages, interests, volunteering, etc. OR <em>Not provided.</em></li>\n</ul>\n\nAdditional rules:\n- No fabricated employers, dates, or education\n- You may rephrase for clarity and impact\n- You may add realistic, conservative metrics if consistent with provided information\n- Preserve truth while optimizing alignment with JD\n- Output must be a single HTML string only — no JSON, no commentary\n"
            },
            "promptType": "define"
          },
          "typeVersion": 2.2
        },
        {
          "id": "bf322d00-2793-491a-8e82-7e550faeadc4",
          "name": "HTML Formatter",
          "type": "n8n-nodes-base.code",
          "position": [
            4800,
            1312
          ],
          "parameters": {
            "jsCode": "// Get the enhanced resume content from the AI agent\nlet custom_resume = $input.first().json.output\n\nconst company_name = $('Set Job Details').first().json.company_name\nconst job_title = $('Set Job Details').first().json.job_title\nconst job_id = $('Set Job Details').first().json.job_id\n\nconst resume_title =  company_name + ' - ' +  job_title + ' Resume - ' + job_id\n// Clean up any unwanted HTML artifacts or markdown formatting\nconst custom_resume_formatted = custom_resume\n    .replace(/^```html\\s*/i, '')  // Remove opening ```html\n    .replace(/\\s*```\\s*$/i, '')   // Remove closing ```\n    .replace(/^`html\\s*/i, '')    // Remove `html\n    .replace(/^html\\s*/i, '')     // Remove html at start\n    .replace(/^\\s*}\\s*html\\s*/i, '') // Remove }html\n    .replace(/^\\s*`\\s*/i, '')     // Remove leading backticks\n    .trim();\n\n// HTML template with styling to match original resume format exactly\nconst custom_resume_html = `\n<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"UTF-8\">\n    <style>\n        body {\n            font-family: Arial, sans-serif;\n            max-width: 800px;\n            margin: 0 auto;\n            padding: 40px;\n            line-height: 1.3;\n            color: #333;\n            background-color: white;\n            font-size: 14px;\n        }\n        \n        h1 {\n            font-size: 28px;\n            font-weight: bold;\n            margin-bottom: 8px;\n            margin-top: 0;\n            color: #000;\n        }\n        \n        .job-title {\n            font-size: 16px;\n            font-weight: bold;\n            margin-bottom: 15px;\n            margin-top: 5px;\n            color: #333;\n        }\n        \n        .contact-section {\n            margin-bottom: 20px;\n        }\n        \n        .contact-section strong {\n            font-weight: bold;\n            display: block;\n            margin-bottom: 8px;\n        }\n        \n        .contact-section ul {\n            list-style: disc;\n            margin-left: 18px;\n            margin-top: 0;\n            margin-bottom: 0;\n            padding: 0;\n        }\n        \n        .contact-section li {\n            margin-bottom: 2px;\n            line-height: 1.2;\n        }\n        \n        h2 {\n            font-size: 16px;\n            font-weight: bold;\n            margin-top: 20px;\n            margin-bottom: 10px;\n            color: #000;\n        }\n        \n        .job-entry {\n            margin-bottom: 20px;\n        }\n        \n        .job-title-line {\n            font-weight: bold;\n            margin-bottom: 2px;\n            line-height: 1.2;\n        }\n        \n        .job-company {\n            font-style: italic;\n            margin-bottom: 8px;\n            line-height: 1.2;\n        }\n        \n        .job-duties ul {\n            list-style: disc;\n            margin-left: 18px;\n            margin-top: 5px;\n            margin-bottom: 0;\n            padding: 0;\n        }\n        \n        .job-duties li {\n            margin-bottom: 5px;\n            line-height: 1.3;\n        }\n        \n        p {\n            margin-bottom: 10px;\n            margin-top: 0;\n            text-align: justify;\n            line-height: 1.3;\n        }\n        \n        a {\n            color: #0066cc;\n            text-decoration: none;\n        }\n        \n        a:hover {\n            text-decoration: underline;\n        }\n        \n        .summary {\n            text-align: justify;\n            margin-bottom: 20px;\n            line-height: 1.3;\n        }\n        \n        /* Ensure proper spacing for different elements */\n        strong {\n            font-weight: bold;\n        }\n        \n        em {\n            font-style: italic;\n        }\n        \n        /* Remove extra spacing from nested elements */\n        ul {\n            margin-top: 0;\n            margin-bottom: 0;\n        }\n        \n        li {\n            line-height: 1.3;\n        }\n    </style>\n</head>\n<body>\n    ${custom_resume_formatted}\n</body>\n</html>\n`;\n\n// Return the formatted HTML\nreturn [{ \n    json: { \n        resume_title: resume_title,\n        custom_resume_html: custom_resume_html,\n        custom_resume_formatted: custom_resume_formatted,\n        original_ai_output: custom_resume\n    } \n}];"
          },
          "typeVersion": 2
        },
        {
          "id": "c1a5b728-2f9a-47b5-a109-3a0b3ed01101",
          "name": "Upload Updated Resume",
          "type": "n8n-nodes-base.httpRequest",
          "position": [
            5216,
            1312
          ],
          "parameters": {
            "url": "=https://www.googleapis.com/upload/drive/v3/files/{{ $json.id }}?uploadType=media",
            "body": "={{ $('HTML Formatter').item.json.custom_resume_html }}",
            "method": "PATCH",
            "options": {},
            "sendBody": true,
            "contentType": "raw",
            "jsonHeaders": "{\n  \"content-type\":\"text/html\"\n}",
            "sendHeaders": true,
            "authentication": "predefinedCredentialType",
            "specifyHeaders": "json",
            "nodeCredentialType": "googleDocsOAuth2Api"
          },
          "credentials": {
            "googleDocsOAuth2Api": {
              "id": "lLgokgGVQ6UHx1G3",
              "name": "Google Docs account"
            }
          },
          "typeVersion": 4.2
        },
        {
          "id": "ba6a71e6-7d43-4f89-a956-3d2638573ca9",
          "name": "Format Final Vals",
          "type": "n8n-nodes-base.code",
          "position": [
            5232,
            1504
          ],
          "parameters": {
            "jsCode": "\nconst {\n  company_name,  \n  job_title,\n  job_url,\n  salary_range,\n  time_posted,\n  num_applicants,\n  job_id,\n} = $('Set Job Details').first().json\n\nconst custom_resume_url = 'https://docs.google.com/document/d/'+$input.first().json.id\n\nconst added_on = new Date()\n\nconst data = {\n  company_name,  \n  job_title,\n  job_url,\n  salary_range,\n  time_posted,\n  num_applicants,\n  job_id,\n  custom_resume_url,\n  added_on,\n}\n\n\nreturn {\n  json: data\n}"
          },
          "typeVersion": 2
        },
        {
          "id": "d896cb9d-060d-422b-8ebc-19646d2d7b9d",
          "name": "Format Response",
          "type": "n8n-nodes-base.code",
          "position": [
            5680,
            1168
          ],
          "parameters": {
            "jsCode": "\nfunction composeSubject(jobData, searchedTitles) {\n  const jobCount = jobData.length;\n  const date = new Date().toLocaleDateString(\"en-US\", {\n    month: \"short\",\n    day: \"numeric\",\n    year: \"numeric\",\n  });\n\n  return `${jobCount} ${searchedTitles} jobs scraped — ${date}`;\n}\nfunction composeText(jobData, searchedTitles) {\n  const jobCount = jobData.length;\n\n  let message = `✅ *${jobCount} ${searchedTitles}* jobs fetched that are relevant to you!\\n\\n`;\n  message += `📝 Custom resumes have been created for each role.\\n📊 You can also view everything in the Google Sheet.\\n\\n`;\n\n  jobData.forEach((job, index) => {\n    message += `*Job ${index + 1}:* ${job.company_name} — ${job.job_title}\\n`;\n    message += `🔗 Job Link: ${job.job_url}\\n`;\n    message += `📄 Resume: ${job.custom_resume_url}\\n\\n`;\n  });\n\n  message += `🚀 Keep applying and stay consistent!`;\n\n  return message;\n}\nfunction composeHTML(jobData, searchedTitles) {\n  // Job data count + formatted date\n  const date = new Date().toLocaleDateString(\"en-US\", {\n    year: \"numeric\",\n    month: \"long\",\n    day: \"numeric\"\n  });\n\n  const jobCount = jobData.length;\n\n  // HTML table rows\n  const rows = jobData.map((job, idx) => `\n    <tr>\n      <td>${idx + 1}</td>\n      <td>${job.company_name}</td>\n      <td>${job.job_title}</td>\n      <td><a href=\"${job.job_url}\" target=\"_blank\">View Job</a></td>\n      <td>${job.salary_range || \"N/A\"}</td>\n      <td>${job.time_posted}</td>\n      <td>${job.num_applicants}</td>\n      <td><a href=\"${job.custom_resume_url}\" target=\"_blank\">View Resume</a></td>\n    </tr>\n  `).join(\"\");\n\n  // Final HTML body\n  return `\n    <div style=\"font-family: Arial, sans-serif; color: #222;\">\n      <h2>✅ ${jobCount} Job Listings Scraped</h2>\n      <p><strong>Role Searched:</strong> ${searchedTitles}</p>\n      <p><strong>Date:</strong> ${date}</p>\n      <br/>\n\n      <table style=\"border-collapse: collapse; width: 100%; text-align: left;\">\n        <thead>\n          <tr style=\"background-color: #f2f2f2;\">\n            <th>#</th>\n            <th>Company</th>\n            <th>Job Title</th>\n            <th>Job Link</th>\n            <th>Salary Range</th>\n            <th>Posted</th>\n            <th>Applicants</th>\n            <th>Resume Link</th>\n          </tr>\n        </thead>\n        <tbody>\n          ${rows}\n        </tbody>\n      </table>\n\n      <br/>\n      <p>📌 This email was auto-generated by your job scraper.</p>\n    </div>\n  `;\n}\n\n\nconst jobData = $input.all().map(i=>i.json)\nconst searchedTitles = $('Array Of Job Inputs').all().map(i=>i.json.job_title).join(', ')\n\nconst subject = composeSubject(jobData, searchedTitles);\nconst text = composeText(jobData, searchedTitles)\nconst html = composeHTML(jobData, searchedTitles)\n\nreturn {\n  json:{\n    subject,\n    text,\n    html\n  }\n}\n"
          },
          "typeVersion": 2
        },
        {
          "id": "d2c49a11-07fa-4c4c-9840-cabf964c53d6",
          "name": "Send a text message",
          "type": "n8n-nodes-base.telegram",
          "position": [
            5696,
            1376
          ],
          "webhookId": "2e0a65d0-ef08-4d54-803e-907a38f13ef4",
          "parameters": {
            "text": "={{ $json.subject }}",
            "chatId": "={{ $('Set Global Vars').first().json.telegram_chat_id }}",
            "additionalFields": {}
          },
          "credentials": {
            "telegramApi": {
              "id": "BlKzI4sSudPj57bV",
              "name": "AiWithSohail Telegram"
            }
          },
          "typeVersion": 1.2
        },
        {
          "id": "0f15b7a9-d969-458a-918a-d4e3a122206f",
          "name": "Send an email",
          "type": "n8n-nodes-base.mailjet",
          "position": [
            5696,
            1552
          ],
          "parameters": {
            "html": "={{ $json.html }}",
            "text": "={{ $json.text }}",
            "subject": "={{ $json.subject }}",
            "toEmail": "={{ $('Set Global Vars').first().json.to_email }}",
            "fromEmail": "={{ $('Set Global Vars').first().json.from_email }}",
            "additionalFields": {}
          },
          "credentials": {
            "mailjetEmailApi": {
              "id": "goG8DkfduUdx12gE",
              "name": "Mailjet Email account"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "7bd8d4fb-8393-463a-8221-0030dcc7b4c5",
          "name": "Create Enhanced Resume",
          "type": "n8n-nodes-base.googleDocs",
          "position": [
            5008,
            1312
          ],
          "parameters": {
            "title": "={{ $json.resume_title }}",
            "folderId": "12EH1HAOQSV_VFK7kh9A5MI0kSfnis74D"
          },
          "credentials": {
            "googleDocsOAuth2Api": {
              "id": "lLgokgGVQ6UHx1G3",
              "name": "Google Docs account"
            }
          },
          "typeVersion": 2
        },
        {
          "id": "89ae28b2-e8fd-4707-91f0-75a63dee344d",
          "name": "Sticky Note3",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            3408,
            208
          ],
          "parameters": {
            "color": 5,
            "width": 688,
            "height": 672,
            "content": "## 📄 Stage 1: Initial Setup & Configuration\n---\n\n- Get Resume: Retrieves the base resume document content (likely a text string) from a document storage service.\n- Set Global Vars: Establishes essential variables for the automation.\n- Array Of Job Inputs: Defines the job search criteria (e.g., job title, location) which drive the main LinkedIn search loop."
          },
          "typeVersion": 1
        },
        {
          "id": "380ab654-c399-470c-a69e-46166a847fb7",
          "name": "Sticky Note",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            4128,
            224
          ],
          "parameters": {
            "color": 4,
            "width": 1824,
            "height": 656,
            "content": "## 🔍 Stage 2: Job Data Retrieval & Screening\n---\n\n- Loop: Runs the entire search process for every job query defined in the input array.\n- Search & Wait: Executes an external service (Actor) to scrape LinkedIn for jobs, then pauses the workflow until the scraping is finished.\n- Validation: Checks the Actor's status; if it failed or errored, the automation stops.\n- Extraction: If successful, the full list of found jobs is retrieved from the Actor's dataset."
          },
          "typeVersion": 1
        },
        {
          "id": "5654607d-8faa-409f-a3fa-b12cfc1b02da",
          "name": "Sticky Note1",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            3408,
            928
          ],
          "parameters": {
            "color": 5,
            "width": 1968,
            "height": 784,
            "content": "## 🧠 Stage 3: Job Screening & Resume Tailoring\n---\n\n- AI Job Match: Evaluate each job for relevance using the base resume.\n- Conditional Check: Only continue if the job is a good match.\n- AI Resume Tailoring: Create a role-optimized resume using the job description.\n- Resume Formatting & Upload: Convert tailored resume to a file and store it.\n- Communication Prep: Format final results for messaging.\n- Notifications: Send an update (email/Telegram) with the tailored resume attached.\n- Logging & Tracking: Save job application details into a spreadsheet for record-keeping."
          },
          "typeVersion": 1
        },
        {
          "id": "d74b9368-3d6e-44a9-a368-38fe8b23bd81",
          "name": "Sticky Note4",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            5424,
            928
          ],
          "parameters": {
            "color": 4,
            "width": 528,
            "height": 784,
            "content": "## 📧 Stage 4: Save Jobs And Send Updates\n---\n\n- Save Jobs in google sheet with custom resume link\n- Send notification via telegram and mail"
          },
          "typeVersion": 1
        },
        {
          "id": "cb2611a8-394b-448e-a94a-ecfe2f4fab87",
          "name": "Loop Over Jobs",
          "type": "n8n-nodes-base.splitInBatches",
          "position": [
            4176,
            640
          ],
          "parameters": {
            "options": {}
          },
          "typeVersion": 3
        },
        {
          "id": "5aecb421-602f-489f-89e8-f32835a03c1e",
          "name": "Loop Over Unique Jobs",
          "type": "n8n-nodes-base.splitInBatches",
          "position": [
            3456,
            1184
          ],
          "parameters": {
            "options": {}
          },
          "typeVersion": 3
        },
        {
          "id": "f045f435-30c7-4e7c-b598-e3a44c807b1c",
          "name": "Set Job Details",
          "type": "n8n-nodes-base.set",
          "position": [
            3680,
            1328
          ],
          "parameters": {
            "options": {},
            "assignments": {
              "assignments": [
                {
                  "id": "69b33c3c-f9c5-4937-8fcd-68d19ccb6b30",
                  "name": "job_id",
                  "type": "string",
                  "value": "={{ $json.job_id }}"
                },
                {
                  "id": "0d898d14-aa96-416c-bc85-5112ebc2f493",
                  "name": "job_title",
                  "type": "string",
                  "value": "={{ $json.job_title }}"
                },
                {
                  "id": "eaee3ae6-4329-420d-a584-415db1952e44",
                  "name": "job_description",
                  "type": "string",
                  "value": "={{ $json.job_description }}"
                },
                {
                  "id": "ed2fc237-bb2a-4439-9dc7-1997b3b1545b",
                  "name": "job_url",
                  "type": "string",
                  "value": "={{ $json.job_url }}"
                },
                {
                  "id": "6dfdaf0a-dc65-4e39-babb-d613ab18009d",
                  "name": "salary_range",
                  "type": "string",
                  "value": "={{ $json.salary_range }}"
                },
                {
                  "id": "28c76a1e-037e-43b8-b8a8-29b302b08d79",
                  "name": "seniority_level",
                  "type": "string",
                  "value": "={{ $json.seniority_level }}"
                },
                {
                  "id": "69d585d6-de59-48f9-891c-f46ac63f53aa",
                  "name": "company_name",
                  "type": "string",
                  "value": "={{ $json.company_name }}"
                },
                {
                  "id": "ec4705c3-a146-476f-aa64-75b3128f4762",
                  "name": "company_url",
                  "type": "string",
                  "value": "={{ $json.company_url }}"
                },
                {
                  "id": "19a18e1f-89ba-4427-9ace-902b86058e26",
                  "name": "employment_type",
                  "type": "string",
                  "value": "={{ $json.employment_type }}"
                },
                {
                  "id": "e8a4a0e0-bcb0-4f7a-af4a-a5971c5697c1",
                  "name": "industries",
                  "type": "string",
                  "value": "={{ $json.industries }}"
                },
                {
                  "id": "67013fc0-1135-4ac6-a685-c1e296e36477",
                  "name": "apply_url",
                  "type": "string",
                  "value": "={{ $json.apply_url }}"
                },
                {
                  "id": "9cf0f51b-fd76-43d2-8306-13caa2ec2e1a",
                  "name": "num_applicants",
                  "type": "string",
                  "value": "={{ $json.num_applicants }}"
                },
                {
                  "id": "b6809ef9-3ee8-4d73-b7df-556061f69800",
                  "name": "time_posted",
                  "type": "string",
                  "value": "={{ $json.time_posted }}"
                }
              ]
            }
          },
          "typeVersion": 3.4
        },
        {
          "id": "e936edbd-4d80-4d5f-891a-10d7c02a51a0",
          "name": "Filter Relevant Jobs",
          "type": "n8n-nodes-base.filter",
          "position": [
            5216,
            1168
          ],
          "parameters": {
            "options": {},
            "conditions": {
              "options": {
                "version": 2,
                "leftValue": "",
                "caseSensitive": true,
                "typeValidation": "strict"
              },
              "combinator": "and",
              "conditions": [
                {
                  "id": "10b55940-7fae-4ecd-a588-0b155d2b9c33",
                  "operator": {
                    "type": "string",
                    "operation": "exists",
                    "singleValue": true
                  },
                  "leftValue": "={{ $json.job_title }}",
                  "rightValue": ""
                }
              ]
            }
          },
          "typeVersion": 2.2
        },
        {
          "id": "34312f73-1365-404f-bf22-50f21479300f",
          "name": "Append or update row in sheet",
          "type": "n8n-nodes-base.googleSheets",
          "position": [
            5488,
            1168
          ],
          "parameters": {
            "columns": {
              "value": {},
              "schema": [
                {
                  "id": "job_id",
                  "type": "string",
                  "display": true,
                  "removed": false,
                  "required": false,
                  "displayName": "job_id",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "company_name",
                  "type": "string",
                  "display": true,
                  "required": false,
                  "displayName": "company_name",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "job_title",
                  "type": "string",
                  "display": true,
                  "required": false,
                  "displayName": "job_title",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "job_url",
                  "type": "string",
                  "display": true,
                  "required": false,
                  "displayName": "job_url",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "custom_resume_url",
                  "type": "string",
                  "display": true,
                  "required": false,
                  "displayName": "custom_resume_url",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "salary_range",
                  "type": "string",
                  "display": true,
                  "required": false,
                  "displayName": "salary_range",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "time_posted",
                  "type": "string",
                  "display": true,
                  "required": false,
                  "displayName": "time_posted",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "added_on",
                  "type": "string",
                  "display": true,
                  "required": false,
                  "displayName": "added_on",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "num_applicants",
                  "type": "string",
                  "display": true,
                  "required": false,
                  "displayName": "num_applicants",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "output",
                  "type": "string",
                  "display": true,
                  "removed": false,
                  "required": false,
                  "displayName": "output",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                }
              ],
              "mappingMode": "autoMapInputData",
              "matchingColumns": [
                "job_id"
              ],
              "attemptToConvertTypes": false,
              "convertFieldsToString": false
            },
            "options": {},
            "operation": "appendOrUpdate",
            "sheetName": {
              "__rl": true,
              "mode": "list",
              "value": "gid=0",
              "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iuGtOePFdUOk0IOKlhz9HgF6re2UnPjbRsWXVHlNjP8/edit#gid=0",
              "cachedResultName": "Sheet1"
            },
            "documentId": {
              "__rl": true,
              "mode": "list",
              "value": "1iuGtOePFdUOk0IOKlhz9HgF6re2UnPjbRsWXVHlNjP8",
              "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iuGtOePFdUOk0IOKlhz9HgF6re2UnPjbRsWXVHlNjP8/edit?usp=drivesdk",
              "cachedResultName": "N8N - LinkedIn Job Search Custom Resumes"
            }
          },
          "credentials": {
            "googleSheetsOAuth2Api": {
              "id": "TB2tCNqF1GBm2ag8",
              "name": "Google Sheets account"
            }
          },
          "typeVersion": 4.7
        }
      ],
      "active": false,
      "pinData": {},
      "settings": {
        "executionOrder": "v1"
      },
      "versionId": "a436778f-b503-4d80-b5f4-df009d207e16",
      "connections": {
        "Wait": {
          "main": [
            [
              {
                "node": "Get run",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Get run": {
          "main": [
            [
              {
                "node": "If RUNNING",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "If RUNNING": {
          "main": [
            [
              {
                "node": "Wait",
                "type": "main",
                "index": 0
              }
            ],
            [
              {
                "node": "If SUCCEEDED",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "If Relevant": {
          "main": [
            [
              {
                "node": "Resume Tailoring Ai",
                "type": "main",
                "index": 0
              }
            ],
            [
              {
                "node": "Loop Over Unique Jobs",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Format Input": {
          "main": [
            [
              {
                "node": "Run an Actor",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "If SUCCEEDED": {
          "main": [
            [
              {
                "node": "Get dataset items",
                "type": "main",
                "index": 0
              }
            ],
            [
              {
                "node": "Stop and Error",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Job Match AI": {
          "main": [
            [
              {
                "node": "If Relevant",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Run an Actor": {
          "main": [
            [
              {
                "node": "Wait",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Error Trigger": {
          "main": [
            [
              {
                "node": "Send Error Email",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Get a document": {
          "main": [
            [
              {
                "node": "Set Global Vars",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "HTML Formatter": {
          "main": [
            [
              {
                "node": "Create Enhanced Resume",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Loop Over Jobs": {
          "main": [
            [
              {
                "node": "Remove Duplicate Jobs",
                "type": "main",
                "index": 0
              }
            ],
            [
              {
                "node": "Format Input",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Format Response": {
          "main": [
            [
              {
                "node": "Send a text message",
                "type": "main",
                "index": 0
              },
              {
                "node": "Send an email",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Set Global Vars": {
          "main": [
            [
              {
                "node": "Array Of Job Inputs",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Set Job Details": {
          "main": [
            [
              {
                "node": "Job Match AI",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Schedule Trigger": {
          "main": [
            [
              {
                "node": "Get a document",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Format Final Vals": {
          "main": [
            [
              {
                "node": "Loop Over Unique Jobs",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Get dataset items": {
          "main": [
            [
              {
                "node": "Loop Over Jobs",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Array Of Job Inputs": {
          "main": [
            [
              {
                "node": "Loop Over Jobs",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Resume Tailoring Ai": {
          "main": [
            [
              {
                "node": "HTML Formatter",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Filter Relevant Jobs": {
          "main": [
            [
              {
                "node": "Append or update row in sheet",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Loop Over Unique Jobs": {
          "main": [
            [
              {
                "node": "Filter Relevant Jobs",
                "type": "main",
                "index": 0
              }
            ],
            [
              {
                "node": "Set Job Details",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "OpenRouter Chat Model": {
          "ai_languageModel": [
            [
              {
                "node": "Job Match AI",
                "type": "ai_languageModel",
                "index": 0
              },
              {
                "node": "Resume Tailoring Ai",
                "type": "ai_languageModel",
                "index": 0
              }
            ]
          ]
        },
        "Remove Duplicate Jobs": {
          "main": [
            [
              {
                "node": "Loop Over Unique Jobs",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Upload Updated Resume": {
          "main": [
            [
              {
                "node": "Format Final Vals",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Create Enhanced Resume": {
          "main": [
            [
              {
                "node": "Upload Updated Resume",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Structured Output Parser": {
          "ai_outputParser": [
            [
              {
                "node": "Job Match AI",
                "type": "ai_outputParser",
                "index": 0
              }
            ]
          ]
        },
        "Append or update row in sheet": {
          "main": [
            [
              {
                "node": "Format Response",
                "type": "main",
                "index": 0
              }
            ]
          ]
        }
      }
    }