AutomationFlowsAI & RAG › Career Ops - Agent 1 - Job Discovery (multi-category)

Career Ops - Agent 1 - Job Discovery (multi-category)

Career Ops - Agent 1 - Job Discovery (Multi-Category). Uses httpRequest, googleSheets, telegram. Event-driven trigger; 10 nodes.

Event trigger★★★★☆ complexity10 nodesHTTP RequestGoogle SheetsTelegram
AI & RAG Trigger: Event Nodes: 10 Complexity: ★★★★☆ Added:

This workflow follows the Google Sheets → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Career Ops - Agent 1 - Job Discovery (Multi-Category)",
  "nodes": [
    {
      "parameters": {},
      "id": "trigger-node",
      "name": "Manual Trigger (swap for Schedule Trigger once tested)",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        220,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const searchTerms = [\n  'help desk support',\n  'desktop support technician',\n  'IT support specialist',\n  'network support technician',\n  'cybersecurity analyst',\n  'SOC analyst',\n  'security compliance analyst',\n  'IT operations manager',\n  'operations manager',\n  'assistant general manager',\n  'branch manager',\n  'restaurant manager',\n  'customer service manager',\n  'automation specialist',\n  'AI engineer',\n  'warehouse associate',\n  'general laborer',\n  'construction laborer',\n  'field technician'\n];\n\nreturn searchTerms.map(term => ({ json: { what: term } }));"
      },
      "id": "categories-node",
      "name": "Job Search Categories",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        300
      ]
    },
    {
      "parameters": {
        "url": "https://api.adzuna.com/v1/api/jobs/us/search/1",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "app_id",
              "value": "YOUR_ADZUNA_APP_ID"
            },
            {
              "name": "app_key",
              "value": "YOUR_ADZUNA_APP_KEY"
            },
            {
              "name": "what",
              "value": "={{$json.what}}"
            },
            {
              "name": "where",
              "value": "Hartford, CT"
            },
            {
              "name": "distance",
              "value": "40"
            },
            {
              "name": "results_per_page",
              "value": "20"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "batching": {
            "batch": {
              "batchSize": 1,
              "batchInterval": 2000
            }
          }
        }
      },
      "id": "adzuna-search-node",
      "name": "Adzuna Search",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        660,
        300
      ],
      "notes": "SECURITY: move app_id/app_key into an n8n Credential before this runs unattended."
    },
    {
      "parameters": {
        "fieldToSplitOut": "results",
        "options": {}
      },
      "id": "split-node",
      "name": "Split Results",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        880,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const HOME_LAT = 41.9959;\nconst HOME_LNG = -72.3020;\nconst MAX_MILES = 45;\n\nfunction haversineMiles(lat1, lon1, lat2, lon2) {\n  const R = 3958.8;\n  const dLat = (lat2 - lat1) * Math.PI / 180;\n  const dLon = (lon2 - lon1) * Math.PI / 180;\n  const a = Math.sin(dLat / 2) ** 2 +\n    Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *\n    Math.sin(dLon / 2) ** 2;\n  return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n}\n\nconst KEYWORDS = [\n  'active directory', 'azure', 'intune', 'security+', 'servicenow', 'help desk',\n  'desktop support', 'network', 'windows server', 'microsoft 365', 'troubleshooting',\n  'vpn', 'endpoint', 'system administration',\n  'cybersecurity', 'soc analyst', 'incident response', 'vulnerability', 'threat',\n  'compliance', 'risk management',\n  'manager', 'management', 'supervisor', 'team lead', 'operations', 'branch',\n  'general manager', 'leadership', 'staff',\n  'automation', 'ai', 'artificial intelligence', 'workflow', 'process improvement',\n  'customer service', 'customer support', 'crm', 'client relations'\n];\n\nconst LOW_BARRIER_TITLE_MARKERS = [\n  'warehouse', 'laborer', 'labor', 'construction', 'general labor',\n  'driver', 'landscaping', 'installer', 'maintenance worker', 'stock'\n];\n\nfunction isLowBarrier(title) {\n  const t = title.toLowerCase();\n  return LOW_BARRIER_TITLE_MARKERS.some(m => t.includes(m));\n}\n\nfunction classifyCategory(title) {\n  const t = title.toLowerCase();\n  if (t.includes('cyber') || t.includes('soc analyst') || t.includes('security compliance') || t.includes('security analyst')) return 'Cybersecurity';\n  if (t.includes('construction')) return 'Construction';\n  if (t.includes('warehouse') || t.includes('driver') || t.includes('landscaping') || t.includes('installer') || t.includes('maintenance worker') || t.includes('stock') || t.includes('general labor')) return 'General Labor';\n  if (t.includes('manager') || t.includes('supervisor') || t.includes('branch') || t.includes('general manager')) return 'Management';\n  if (t.includes(' ai ') || t.startsWith('ai ') || t.includes('artificial intelligence') || t.includes('automation')) return 'AI/Automation';\n  if (t.includes('customer service') || t.includes('customer support') || t.includes('client relations')) return 'Customer Service';\n  if (t.includes('help desk') || t.includes('desktop support') || t.includes('it support') || t.includes('network') || t.includes('technician') || t.includes('field technician')) return 'IT';\n  return 'Other';\n}\n\nfunction scoreFit(title, description) {\n  const text = (title + ' ' + description).toLowerCase();\n  let hits = 0;\n  for (const kw of KEYWORDS) {\n    if (text.includes(kw)) hits++;\n  }\n  return Math.min(5, Math.max(1, Math.ceil(hits / 2)));\n}\n\nfunction formatSalary(min, max) {\n  if (!min && !max) return '';\n  const fmt = (n) => '$' + Math.round(n).toLocaleString('en-US');\n  if (min && max) return fmt(min) + ' - ' + fmt(max);\n  return fmt(min || max);\n}\n\nconst items = $input.all();\nconst seen = new Set();\nconst output = [];\n\nfor (const item of items) {\n  const job = item.json;\n  if (!job.id || seen.has(job.id)) continue;\n  seen.add(job.id);\n\n  if (!job.latitude || !job.longitude) continue;\n  const distance = haversineMiles(HOME_LAT, HOME_LNG, parseFloat(job.latitude), parseFloat(job.longitude));\n  if (distance > MAX_MILES) continue;\n\n  const title = job.title || '';\n  const lowBarrier = isLowBarrier(title);\n  const keywordScore = scoreFit(title, job.description || '');\n  const fitScore = lowBarrier ? Math.max(3, keywordScore) : keywordScore;\n  const applyUrl = job.redirect_url || '';\n\n  output.push({\n    json: {\n      adzuna_id: String(job.id),\n      title: title,\n      company: job.company ? job.company.display_name : '',\n      location: job.location ? job.location.display_name : '',\n      category: classifyCategory(title),\n      job_type: job.contract_time === 'full_time' ? 'Full-time' : (job.contract_time === 'part_time' ? 'Part-time' : ''),\n      salary_range: formatSalary(job.salary_min, job.salary_max),\n      distance_miles: Math.round(distance),\n      description: job.description || '',\n      apply_url: applyUrl,\n      apply_link_formula: applyUrl ? ('=HYPERLINK(\"' + applyUrl + '\",\"Apply \\u2192\")') : '',\n      created: job.created,\n      low_barrier_role: lowBarrier,\n      fit_score: fitScore\n    }\n  });\n}\n\nreturn output;"
      },
      "id": "filter-score-node",
      "name": "Dedupe + Filter Distance + Score Fit",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "leftValue": "={{$json.fit_score}}",
              "rightValue": 3,
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "filter-fit-node",
      "name": "Keep Fit Score 3+",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2,
      "position": [
        1320,
        300
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "1JdchogQzAHxzXEGD2STDYoz2O4V_qB7nSDytFAbrTcg",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Applications",
          "mode": "name"
        },
        "operation": "appendOrUpdate",
        "columns": {
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Adzuna ID"
          ],
          "value": {
            "Date Found": "={{$now.format('yyyy-MM-dd')}}",
            "Job Title": "={{$json.title}}",
            "Company": "={{$json.company}}",
            "Location": "={{$json.location}}",
            "Category": "={{$json.category}}",
            "Job Type": "={{$json.job_type}}",
            "Salary Range": "={{$json.salary_range}}",
            "Fit Score": "={{$json.fit_score}}",
            "Source": "Adzuna",
            "Apply Link": "={{$json.apply_link_formula}}",
            "Job Description": "={{$json.description}}",
            "Status": "Ready to Apply",
            "Adzuna ID": "={{$json.adzuna_id}}"
          }
        },
        "options": {
          "valueInputMode": "USER_ENTERED"
        }
      },
      "id": "append-sheet-node",
      "name": "Append or Update Tracker Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1540,
        300
      ],
      "notes": "Matches on Adzuna ID - re-running this daily is now safe from creating duplicate rows. LIMITATION: if the same job still appears in Adzuna's results on a later run, this WILL overwrite that row's Status back to 'Ready to Apply', even if you'd already marked it Applied. This only happens if a specific listing stays live in Adzuna's index across multiple runs. Acceptable for now - if it becomes a real problem, the fix is a lookup-and-skip step before the write, which we can add later."
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "days",
              "daysInterval": 1,
              "triggerAtHour": 7,
              "triggerAtMinute": 0
            }
          ]
        }
      },
      "id": "schedule-trigger-node",
      "name": "Daily Schedule (7:00 AM)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        220,
        460
      ]
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst count = items.length;\n\nif (count === 0) {\n  return [{ json: { send: false, message: '' } }];\n}\n\n// Sort by fit score, highest first, show top 10 in the message\nconst sorted = [...items].sort((a, b) => (b.json.fit_score || 0) - (a.json.fit_score || 0));\nconst top = sorted.slice(0, 10);\n\nlet msg = `\\ud83d\\udd0d Career Ops Daily Digest\\n${count} new job match${count === 1 ? '' : 'es'} added today\\n\\n`;\n\ntop.forEach((item, i) => {\n  const j = item.json;\n  msg += `${i + 1}. ${j.title}\\n`;\n  msg += `   ${j.company} \\u2014 ${j.location}\\n`;\n  msg += `   Fit: ${j.fit_score}/5${j.salary_range ? ' \\u2014 ' + j.salary_range : ''}\\n`;\n  msg += `   Apply: ${j.apply_url}\\n\\n`;\n});\n\nif (count > 10) {\n  msg += `+ ${count - 10} more \\u2014 see the full list in your tracker.\\n\\n`;\n}\n\nmsg += `Full tracker: https://docs.google.com/spreadsheets/d/1JdchogQzAHxzXEGD2STDYoz2O4V_qB7nSDytFAbrTcg/edit`;\n\nreturn [{ json: { send: true, message: msg } }];"
      },
      "id": "digest-format-node",
      "name": "Format Telegram Digest",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1760,
        300
      ]
    },
    {
      "parameters": {
        "chatId": "7864873212",
        "text": "={{ $json.message }}",
        "additionalFields": {}
      },
      "id": "telegram-send-node",
      "name": "Send Telegram Digest",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1980,
        300
      ],
      "notes": "Set credential to your existing Telegram bot (Bode_First_OpenClaw_bot / @teslims_bot) - same one your OpenClaw agents already use. If not already saved in this n8n instance, create it with the bot token from BotFather."
    }
  ],
  "connections": {
    "Manual Trigger (swap for Schedule Trigger once tested)": {
      "main": [
        [
          {
            "node": "Job Search Categories",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Job Search Categories": {
      "main": [
        [
          {
            "node": "Adzuna Search",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Adzuna Search": {
      "main": [
        [
          {
            "node": "Split Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Results": {
      "main": [
        [
          {
            "node": "Dedupe + Filter Distance + Score Fit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dedupe + Filter Distance + Score Fit": {
      "main": [
        [
          {
            "node": "Keep Fit Score 3+",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Keep Fit Score 3+": {
      "main": [
        [
          {
            "node": "Append to Tracker Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Schedule (7:00 AM)": {
      "main": [
        [
          {
            "node": "Job Search Categories",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append or Update Tracker Sheet": {
      "main": [
        [
          {
            "node": "Format Telegram Digest",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Telegram Digest": {
      "main": [
        [
          {
            "node": "Send Telegram Digest",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Career Ops - Agent 1 - Job Discovery (Multi-Category). Uses httpRequest, googleSheets, telegram. Event-driven trigger; 10 nodes.

Source: https://github.com/Bode-N-Ayansina/career-ops-automation/blob/main/career-ops-discovery-agent.n8n.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Finance RAG — Retrieval Agent. Uses telegramTrigger, httpRequest, googleSheets, gmail. Event-driven trigger; 11 nodes.

Telegram Trigger, HTTP Request, Google Sheets +2
AI & RAG

Agent-Business-Ai. Uses telegramTrigger, httpRequest, googleSheets, telegram. Event-driven trigger; 6 nodes.

Telegram Trigger, HTTP Request, Google Sheets +1
AI & RAG

This workflow creates a multi-talented AI assistant named Simran that interacts with users via Telegram. It can handle text and voice messages, understand the user's intent, and perform various tasks.

MongoDB, Chain Llm, Google Gemini Chat +11
AI & RAG

Generate AI viral videos with NanoBanana & VEO3, shared on socials via Blotato 2. Uses @blotato/n8n-nodes-blotato, googleSheets, lmChatOpenAi, toolThink. Event-driven trigger; 94 nodes.

@Blotato/N8N Nodes Blotato, Google Sheets, OpenAI Chat +9
AI & RAG

This project is a template for building a complete academic virtual assistant using n8n. It connects to Telegram, answers frequently asked questions by querying MongoDB, keeps the community informed a

Telegram, MongoDB, Telegram Trigger +6