{
  "id": "5PkDOabH6WKqQQWO",
  "name": "Job Application Automation Template(2-Parts)",
  "tags": [],
  "nodes": [
    {
      "id": "c7cb8211-170a-450a-ac76-973ef872c656",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        368,
        -768
      ],
      "parameters": {
        "width": 848,
        "height": 896,
        "content": "## Job Application Automation Template(2-Parts)\n\n### How it works\n\n1. The workflow starts with a CV upload form trigger.\n2. The uploaded CV is then validated and processed.\n3. Jobs are fetched from Remotive and Arbeitnow.\n4. CV profiles and sent log are retrieved.\n5. Match candidates are built and Groq response is obtained.\n6. The response is logged and scored.\n7. Matches are collected and grouped by profile.\n8. A digest email is sent.\n\n### Setup steps\n\n- - [ ] Set up CV upload form trigger\n- - [ ] Configure job fetching nodes\n- - [ ] Set up CV profiles and sent log nodes\n- - [ ] Configure match candidates and Groq response nodes\n- - [ ] Set up log and score nodes\n- - [ ] Configure collect and group matches nodes\n- - [ ] Set up send digest email node\n\n### Customization\n\nThe workflow can be customized by modifying the job fetching nodes to fetch jobs from different sources, or by changing the criteria for building match candidates."
      },
      "typeVersion": 1
    },
    {
      "id": "2b464b77-a4d4-41cf-a94b-c296b3390556",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1280,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 1072,
        "height": 496,
        "content": "## CV Upload\n\nUpload and validate CV"
      },
      "typeVersion": 1
    },
    {
      "id": "bd11c9d5-4bb1-47d5-9f9b-1ccf98e737ce",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1120,
        448
      ],
      "parameters": {
        "color": 7,
        "width": 496,
        "height": 480,
        "content": "## Job Fetching\n\nFetch jobs from Remotive and Arbeitnow"
      },
      "typeVersion": 1
    },
    {
      "id": "2f08bf63-b6d5-4ba5-805b-546646597088",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1648,
        496
      ],
      "parameters": {
        "color": 7,
        "height": 560,
        "content": "## CV Profiles and Sent Log\n\nGet CV profiles and sent log"
      },
      "typeVersion": 1
    },
    {
      "id": "8e726702-89f8-4b3d-9028-91e56fd6e8f6",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1920,
        624
      ],
      "parameters": {
        "color": 7,
        "width": 624,
        "height": 432,
        "content": "## Match Candidates\n\nBuild match candidates and get Groq response"
      },
      "typeVersion": 1
    },
    {
      "id": "3163f9e8-8900-4b66-98aa-7cda389da6b1",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2576,
        512
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 272,
        "content": "## Log and Score\n\nLog to sent log and check score"
      },
      "typeVersion": 1
    },
    {
      "id": "81aa63fa-dcf7-4875-83b1-4d478094c181",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2992,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 272,
        "content": "## Collect and Group Matches\n\nCollect all matches and group by profile"
      },
      "typeVersion": 1
    },
    {
      "id": "29f49da0-8442-43b2-8255-376c50b8f515",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3408,
        432
      ],
      "parameters": {
        "color": 7,
        "height": 272,
        "content": "## Send Digest Email\n\nSend digest email"
      },
      "typeVersion": 1
    },
    {
      "id": "3afdb83e-6cc6-4df9-879b-d53af74d7419",
      "name": "Upload CV Form",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        1328,
        64
      ],
      "parameters": {
        "path": "CV_Upload",
        "options": {},
        "formTitle": "Add Your CV Profile",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Profile Name (e.g. n8n Automation Engineer)"
            },
            {
              "fieldLabel": "Target Keywords (comma-separated)"
            },
            {
              "fieldLabel": "Notify Email"
            },
            {
              "fieldType": "dropdown",
              "fieldLabel": "Work Preference",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Remote"
                  },
                  {
                    "option": "Onsite"
                  },
                  {
                    "option": "Hybrid"
                  },
                  {
                    "option": "Any"
                  }
                ]
              }
            },
            {
              "fieldType": "file",
              "fieldLabel": "Upload CV (PDF or DOCX)",
              "acceptFileTypes": ".pdf,.docx"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "e413774f-ebcf-4f1d-a582-772923ba4582",
      "name": "Check File Type",
      "type": "n8n-nodes-base.if",
      "position": [
        1552,
        64
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $binary['Upload_CV__PDF_or_DOCX_'].fileExtension }}",
              "rightValue": "pdf"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "419ef8b6-eff2-43ad-8bdf-19645ab054d0",
      "name": "Extract PDF Data",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        1760,
        -48
      ],
      "parameters": {
        "options": {},
        "operation": "pdf",
        "binaryPropertyName": "Upload_CV__PDF_or_DOCX_"
      },
      "typeVersion": 1
    },
    {
      "id": "c182ec98-badb-4e08-948c-e14ad8ada7e0",
      "name": "Extract DOCX",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        1760,
        176
      ],
      "parameters": {
        "operation": "docx"
      },
      "typeVersion": 1
    },
    {
      "id": "7aa3e8f0-4057-4e65-befc-6cabc47bc523",
      "name": "Build Profile",
      "type": "n8n-nodes-base.code",
      "position": [
        1984,
        64
      ],
      "parameters": {
        "jsCode": "const form = $('Upload CV Form').first().json;\nconst extracted = $input.first().json;\nreturn [{\n  json: {\n    profile_name: form['Profile Name (e.g. n8n Automation Engineer)'],\n    target_keywords: form['Target Keywords (comma-separated)'],\n    notify_email: form['Notify Email'],\n    work_preference: form['Work Preference'],\n    resume_text: (extracted.text || '').replace(/\\s+/g, ' ').trim()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "fe1d641d-8a70-47e1-88e3-409cf28cef21",
      "name": "Save Profile",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2208,
        64
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "profile_name"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 273481390,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=273481390",
          "cachedResultName": "CV_Profiles"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
          "cachedResultName": "Job app job"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "9958a898-1771-4af8-9b3e-29b01f436417",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1168,
        672
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "50d9d7da-acf6-4e30-94c7-fb4cc88f19d2",
      "name": "Fetch Remotive",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1472,
        560
      ],
      "parameters": {
        "url": "https://remotive.com/api/remote-jobs?limit=50",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "837ed826-c8c0-4ec4-a84c-5f1aaa5e7132",
      "name": "Fetch Arbeitnow",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1472,
        768
      ],
      "parameters": {
        "url": "https://www.arbeitnow.com/api/job-board-api",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "b6b4ec19-3c25-4f65-9539-dad7d018875a",
      "name": "Get Profiles",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1696,
        672
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 273481390,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=273481390",
          "cachedResultName": "CV_Profiles"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
          "cachedResultName": "Job app job"
        }
      },
      "typeVersion": 4.5,
      "alwaysOutputData": true
    },
    {
      "id": "a1ad94e2-ee7c-422b-9963-01d9b65ebc55",
      "name": "Get Sent Log",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1712,
        896
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1354601517,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=1354601517",
          "cachedResultName": "Sent_Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
          "cachedResultName": "Job app job"
        }
      },
      "typeVersion": 4.5,
      "alwaysOutputData": true
    },
    {
      "id": "3c5b0670-449d-45cd-81a6-98e519ce8f00",
      "name": "Build Matches",
      "type": "n8n-nodes-base.code",
      "notes": "Returns 0 items when no jobs pass the keyword/work-preference filter \u2014 this is expected, not an error.",
      "onError": "continueErrorOutput",
      "position": [
        1968,
        896
      ],
      "parameters": {
        "jsCode": "const remotive = ($('Fetch Remotive').first().json.jobs || []).map(j => ({\n  job_id: 'remotive_' + j.id,\n  title: j.title,\n  company: j.company_name,\n  description: (j.description || '').replace(/<[^>]*>/g, '').slice(0, 2000),\n  url: j.url,\n  remote: true\n}));\n\nconst arbeitnow = ($('Fetch Arbeitnow').first().json.data || []).map(j => ({\n  job_id: 'arbeitnow_' + j.slug,\n  title: j.title,\n  company: j.company_name,\n  description: (j.description || '').replace(/<[^>]*>/g, '').slice(0, 2000),\n  url: j.url,\n  remote: !!j.remote\n}));\n\nconst allJobs = [...remotive, ...arbeitnow];\n\nconst profiles = $('Get Profiles').all().map(i => i.json);\nconst sentLog = new Set($('Get Sent Log').all().map(i => `${i.json.job_id}|${i.json.profile_name}`));\n\nconst candidates = [];\nfor (const profile of profiles) {\n  const keywords = (profile.target_keywords || '').toLowerCase().split(',').map(k => k.trim()).filter(Boolean);\n  const pref = (profile.work_preference || 'Any').toLowerCase();\n  for (const job of allJobs) {\n    const key = `${job.job_id}|${profile.profile_name}`;\n    if (sentLog.has(key)) continue;\n    if (pref === 'remote' && !job.remote) continue;\n    if (pref === 'onsite' && job.remote) continue;\n    const matchesKeyword = keywords.length === 0 || keywords.some(k => job.title.toLowerCase().includes(k));\n    if (matchesKeyword) {\n      candidates.push({\n        json: {\n          job_id: job.job_id,\n          job_title: job.title,\n          company: job.company,\n          job_description: job.description,\n          job_url: job.url,\n          profile_name: profile.profile_name,\n          resume_text: profile.resume_text,\n          notify_email: profile.notify_email\n        }\n      });\n    }\n  }\n}\nreturn candidates;"
      },
      "notesInFlow": true,
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "0bd7c9b4-d2bb-45d5-871b-69b490d3c12e",
      "name": "Groq Match",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2192,
        736
      ],
      "parameters": {
        "url": "https://api.groq.com/openai/v1/chat/completions",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({\n  model: \"llama-3.3-70b-versatile\",\n  messages: [\n    { role: \"system\", content: \"You are a strict ATS reviewer. Score honestly, do not inflate for superficial keyword overlap. A candidate must match the CORE FUNCTION of the role, not just mention similar tools, to score above 60. If the job's primary function differs from the candidate's target role, score below 50 regardless of skill overlap. Always populate missing_keywords with the specific gaps causing a low score, even when ats_score is below 50. Reply ONLY with valid JSON: {\\\"ats_score\\\": number 0-100, \\\"missing_keywords\\\": [strings], \\\"tailored_cover_letter\\\": string}. No markdown.\" },\n    { role: \"user\", content: \"JOB TITLE: \" + $json.job_title + \"\\nCOMPANY: \" + $json.company + \"\\nJOB DESCRIPTION:\\n\" + $json.job_description + \"\\n\\nRESUME:\\n\" + $json.resume_text }\n  ],\n  temperature: 0.3\n}) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b1fda26d-99c4-4bc3-8b73-9873ff8df421",
      "name": "Parse Groq",
      "type": "n8n-nodes-base.code",
      "notes": "Returns 0 items when Build Match Candidates sent a placeholder/empty item \u2014 this guard clause is intentional, not a failure. See job_id check at top of code.",
      "onError": "continueErrorOutput",
      "position": [
        2400,
        736
      ],
      "parameters": {
        "jsCode": "const input = $input.first().json;\nconst src = $('Build Matches').first().json;\n\n// Guard: skip placeholder/empty items (e.g. from Always Output Data on an empty branch)\nif (!src.job_id) {\n  return [];\n}\n\nlet parsed;\ntry { parsed = JSON.parse(input.choices[0].message.content); }\ncatch (e) { parsed = { ats_score: 0, missing_keywords: [], tailored_cover_letter: '' }; }\n\nreturn [{\n  json: {\n    job_id: src.job_id,\n    job_title: src.job_title,\n    company: src.company,\n    job_url: src.job_url,\n    profile_name: src.profile_name,\n    notify_email: src.notify_email,\n    ats_score: parsed.ats_score,\n    missing_keywords: (parsed.missing_keywords || []).join(', '),\n    cover_letter: parsed.tailored_cover_letter,\n    status: parsed.ats_score >= 70 ? 'READY' : 'SKIP',\n    timestamp: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c1851847-f2ac-4a52-ae0b-5739ed72a963",
      "name": "Log Sent",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2624,
        624
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "job_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1354601517,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=1354601517",
          "cachedResultName": "Sent_Log"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
          "cachedResultName": "Job app job"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "b7232939-3ae0-4b6f-9340-40e5b89cb0f3",
      "name": "Check Score",
      "type": "n8n-nodes-base.if",
      "position": [
        2816,
        624
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ba06c36b-b7c6-4ff4-9ed2-143efb893cb7",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.ats_score }}",
              "rightValue": 70
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "f50003eb-71df-4d37-aa6c-7eeaf7223228",
      "name": "Collect Matches",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        3040,
        544
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "f1a12491-d5be-4072-a49f-f966b417cb7f",
      "name": "Group Profiles",
      "type": "n8n-nodes-base.code",
      "position": [
        3232,
        544
      ],
      "parameters": {
        "jsCode": "const items = $input.first().json.data || $input.all().map(i => i.json);\nconst byProfile = {};\nfor (const m of items) {\n  if (!byProfile[m.profile_name]) byProfile[m.profile_name] = { profile_name: m.profile_name, notify_email: m.notify_email, matches: [] };\n  byProfile[m.profile_name].matches.push(m);\n}\nreturn Object.values(byProfile).map(p => ({ json: p }));"
      },
      "typeVersion": 2
    },
    {
      "id": "cdd031dd-f06c-47da-843d-de38be35ff1f",
      "name": "Send Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        3456,
        544
      ],
      "parameters": {
        "sendTo": "={{ $json.notify_email }}",
        "message": "=<h2>Matches for {{ $json.profile_name }}</h2>{{ $json.matches.map(m => `<hr><b>${m.job_title}</b> at ${m.company} \u2014 Score: ${m.ats_score}/100<br><a href=\"${m.job_url}\">View & Apply</a><br><br><b>Suggested Cover Letter:</b><br>${m.cover_letter.replace(/\\n/g, '<br>')}`).join('') }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "=\ud83c\udfaf {{ $json.matches.length }} New Job Match(es) \u2014 {{ $json.profile_name }}"
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "de055ae3-7044-460a-b7d9-7889185fc7a3",
  "nodeGroups": [],
  "connections": {
    "Log Sent": {
      "main": [
        [
          {
            "node": "Check Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Groq Match": {
      "main": [
        [
          {
            "node": "Parse Groq",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Groq": {
      "main": [
        [
          {
            "node": "Log Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Score": {
      "main": [
        [
          {
            "node": "Collect Matches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract DOCX": {
      "main": [
        [
          {
            "node": "Build Profile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Profiles": {
      "main": [
        [
          {
            "node": "Get Sent Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Sent Log": {
      "main": [
        [
          {
            "node": "Build Matches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Matches": {
      "main": [
        [
          {
            "node": "Groq Match",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Profile": {
      "main": [
        [
          {
            "node": "Save Profile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Remotive": {
      "main": [
        [
          {
            "node": "Fetch Arbeitnow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Group Profiles": {
      "main": [
        [
          {
            "node": "Send Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload CV Form": {
      "main": [
        [
          {
            "node": "Check File Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check File Type": {
      "main": [
        [
          {
            "node": "Extract PDF Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Extract DOCX",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect Matches": {
      "main": [
        [
          {
            "node": "Group Profiles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Arbeitnow": {
      "main": [
        [
          {
            "node": "Get Profiles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract PDF Data": {
      "main": [
        [
          {
            "node": "Build Profile",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Fetch Remotive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}