{
  "id": "r1WHh7zhA2TDwDlb",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Personal Job Market Intelligence System",
  "tags": [],
  "nodes": [
    {
      "id": "a16b959a-7eec-470f-a433-bcc5bb690595",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -144,
        -96
      ],
      "parameters": {
        "width": 624,
        "height": 736,
        "content": "## Personal Job Market Intelligence System\n\n  ### How it works\n\n  1. The workflow starts by receiving a message via Telegram.\n  2. It sets configuration variables for further processing.\n  3. It checks if the Telegram user ID is authorized.\n  4. Depending on the message content, it routes to either extract job data or generate a report.\n  5. It updates a Google Sheet with job data and confirms via Telegram or analyzes job patterns and sends a report.\n\n  ### Setup steps\n\n  - [ ] Create a Telegram bot via @BotFather and copy the API token. Add it as a Telegram credential in n8n.\n  - [ ] Set Google Sheets API credentials and permissions (Service Account recommended).\n  - [ ] Set variables in 'Set Config Variables' node: paste your Google Sheet ID and sheet name.\n  - [ ] In your Google Sheet, add these headers in row 1: Date, Company, Role, Seniority, Hard Requirements, Tech\n  Stack, Salary, Location, Domain, Key Signal\n  - [ ] In 'Check Telegram User ID' node, replace the hardcoded ID with your own Telegram user ID.\n\n  ### How to use\n\n  - **Log a job:** While browsing LinkedIn or any job board, copy the full job description and paste it into your\n  Telegram chat with the bot. Claude will extract structured data and log it to your Google Sheet automatically. You\n  will receive a confirmation message.\n  - **Get a report:** Send `/report` to the bot. Claude will analyze all logged jobs and send back a summary of top\n  skills, tech stack trends, salary ranges, seniority distribution, and a key market insight.\n\n  ### Customization\n\nYou can customize which jobs or patterns to focus on by adjusting the AI prompt in the 'Claude Extract Job Info' node or the report format in 'Claude Analyze Job Patterns'"
      },
      "typeVersion": 1
    },
    {
      "id": "a3f94cd2-d4d6-480c-9256-5011a60a4bdf",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        560,
        256
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 304,
        "content": "## Receive message and configure\n\nReceives Telegram messages and initializes workflow variables."
      },
      "typeVersion": 1
    },
    {
      "id": "a59e6232-7e94-424a-aa84-d6ba2e77e125",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1008,
        256
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 304,
        "content": "## User authentication and routing\n\nAuthenticates user and routes to appropriate job or report process."
      },
      "typeVersion": 1
    },
    {
      "id": "70298028-9f96-469c-a1b1-92ed91ab0d9c",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1456,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 976,
        "height": 272,
        "content": "## Job data extraction\n\nExtracts job data using AI and logs it in Google Sheets, then confirms."
      },
      "typeVersion": 1
    },
    {
      "id": "c5422e78-3c0c-4a0c-ac28-3929194ac238",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 752,
        "height": 272,
        "content": "## Report generation and sending\n\nReads all jobs, generates patterns via AI, and sends report through Telegram."
      },
      "typeVersion": 1
    },
    {
      "id": "54fc3b7b-9ee1-4ec0-bf40-0dc282ef5d87",
      "name": "When Telegram Message Received",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        608,
        384
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "d9738cd5-9db2-4060-9702-18e8530a8f24",
      "name": "Set Config Variables",
      "type": "n8n-nodes-base.set",
      "position": [
        832,
        384
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "3156a863-beb0-4bc8-a88f-f3b1e2840eb3",
              "name": "SHEET_PERSONAL_JOB_MARKET_INTEL_ID",
              "type": "string",
              "value": "<YOUR-VALUE-HERE>"
            },
            {
              "id": "3b1498b8-0714-472d-a7e2-c0ef49f49da0",
              "name": "SHEET_PERSONAL_JOB_MARKET_INTEL_SHEET",
              "type": "string",
              "value": "Sheet1"
            },
            {
              "id": "305e9368-5977-4837-9533-bcd1a84c867f",
              "name": "TELEGRAM_USER_ID",
              "type": "number",
              "value": 1234567890
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a47af54f-eecf-4d65-b35e-1590d706b8a1",
      "name": "Check Telegram User ID",
      "type": "n8n-nodes-base.if",
      "position": [
        1056,
        384
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "8882b696-1efe-4369-bb45-b93223a6dcda",
              "operator": {
                "type": "number",
                "operation": "equals"
              },
              "leftValue": "={{ $('When Telegram Message Received').item.json.message.chat.id }}",
              "rightValue": "={{ $json.TELEGRAM_USER_ID }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "62c640b2-6daf-4123-8411-07f90b97e711",
      "name": "Route by Message Type",
      "type": "n8n-nodes-base.if",
      "position": [
        1280,
        384
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "a68a6869-2db2-46d9-b48c-90fa78646216",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('When Telegram Message Received').item.json.message.text }}",
              "rightValue": "/report"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "490a2ecc-3498-4be4-b622-17168157c7f7",
      "name": "Claude Extract Job Info",
      "type": "@n8n/n8n-nodes-langchain.anthropic",
      "position": [
        1504,
        384
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "claude-haiku-4-5-20251001",
          "cachedResultName": "claude-haiku-4-5-20251001"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=  Extract structured data from this job posting and return ONLY valid JSON, no markdown, no code blocks,\n   no explanation.\n\n  Job Posting:\n  {{ $('When Telegram Message Received').item.json.message.text }}\n\n  Return this exact JSON structure:\n  {\n    \"company\": \"string\",\n    \"role\": \"string\",\n    \"seniority\": \"Junior|Mid|Senior|Lead|Principal\",\n    \"hard_requirements\": \"comma-separated list\",\n    \"tech_stack\": \"comma-separated list\",\n    \"salary\": \"range or empty string if not mentioned\",\n    \"location\": \"Remote|Hybrid|On-site, city if mentioned\",\n    \"domain\": \"Fintech|SaaS|Retail|Healthcare|Other\",\n    \"key_signal\": \"one sentence \u2014 what makes this role unique or what they really care about\"\n  }"
            }
          ]
        }
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b9adade9-90d0-409a-8ae6-64c0d7ab7538",
      "name": "Parse JSON for Sheets",
      "type": "n8n-nodes-base.code",
      "position": [
        1856,
        384
      ],
      "parameters": {
        "jsCode": "const text = $input.item.json.content[0].text;\n  const cleaned = text.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n  return { json: JSON.parse(cleaned) };"
      },
      "typeVersion": 2
    },
    {
      "id": "d3c0082c-fc42-4dfc-b3ea-7fbd4a89e984",
      "name": "Append Job Data to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2080,
        384
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $now.toFormat('yyyy-MM-dd') }}",
            "Role": "={{ $json.role }}",
            "Domain": "={{ $json.domain }}",
            "Salary": "={{ $json.salary }}",
            "Company": "={{ $json.company }}",
            "Location": "={{ $json.location }}",
            "Seniority": "={{ $json.seniority }}",
            "Key Signal": "={{ $json.key_signal }}",
            "Tech Stack": "={{ $json.tech_stack }}",
            "Hard Requirements": "={{ $json.hard_requirements }}"
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Company",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Company",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Role",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Role",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Seniority",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Seniority",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Hard Requirements",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Hard Requirements",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Tech Stack",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Tech Stack",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Salary",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Salary",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Location",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Location",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Domain",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Domain",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Key Signal",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Key Signal",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Config Variables').item.json.SHEET_PERSONAL_JOB_MARKET_INTEL_SHEET }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Config Variables').item.json.SHEET_PERSONAL_JOB_MARKET_INTEL_ID }}"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "8e9accb2-609a-4559-b592-228689cf45ea",
      "name": "Send Telegram Log Confirmation",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2288,
        384
      ],
      "parameters": {
        "text": "=\u2705 *Logged*    *{{ $json.Role }}* at *{{ $json.Company }}*   Seniority: {{ $json.Seniority }}   Domain: {{ $json.Domain }}",
        "chatId": "={{ $('When Telegram Message Received').item.json.message.chat.id }}",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "719b00b6-cc0b-4d7a-a95d-daf39368cfd1",
      "name": "Read All Jobs from Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1520,
        16
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $('Set Config Variables').item.json.SHEET_PERSONAL_JOB_MARKET_INTEL_SHEET }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Set Config Variables').item.json.SHEET_PERSONAL_JOB_MARKET_INTEL_ID }}"
        },
        "authentication": "serviceAccount"
      },
      "credentials": {
        "googleApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "8cd6100d-1ff6-477f-ae17-dd773034189f",
      "name": "Claude Analyze Job Patterns",
      "type": "@n8n/n8n-nodes-langchain.anthropic",
      "position": [
        1728,
        16
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "claude-haiku-4-5-20251001",
          "cachedResultName": "claude-haiku-4-5-20251001"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=You are analyzing a personal job market database. Below is structured data from {{ $input.all().length\n   }} job postings. Analyze the patterns and return a formatted report.\n\n  Data:\n  {{ JSON.stringify($input.all().map(item => item.json)) }}\n\n  Return a clear report with these sections:\n  1. **Top Skills** \u2014 most frequently required across all postings\n  2. **Tech Stack Trends** \u2014 what technologies appear most\n  3. **Salary Range** \u2014 distribution and average if available\n  4. **Seniority Distribution** \u2014 Junior/Mid/Senior breakdown\n  5. **Domains** \u2014 which industries are hiring most\n  6. **Key Signal** \u2014 one insight about what the market is actually looking for right now\n\nReturn a SHORT Telegram-friendly report using only: *bold*, bullet points, and emojis. No tables, no\n  markdown headers, no long explanations. Max 300 words.\n\n  Format exactly like this:\n  \ud83d\udcca *Job Market Report \u2014 [X] postings*\n\n  \ud83d\udd27 *Top Skills*\n  \u2022 Skill 1 (X/X roles)\n  \u2022 Skill 2 (X/X roles)\n\n  \ud83d\udcb0 *Salary Range*\n  \u2022 Mid: \u20acX\u2013\u20acX\n  \u2022 Senior: \u20acX\u2013\u20acX\n\n  \ud83d\udcc8 *Seniority*\n  \u2022 Mid: X% | Senior: X% | Junior: 0%\n\n  \ud83c\udfed *Domains*\n  \u2022 Domain 1, Domain 2\n\n  \ud83d\udca1 *Key Signal*\n  One sentence. What the market actually wants right now."
            }
          ]
        }
      },
      "credentials": {
        "anthropicApi": {
          "name": "<your credential>"
        }
      },
      "executeOnce": true,
      "typeVersion": 1
    },
    {
      "id": "fb8ac2b1-e375-4cdf-8f2d-91742984b730",
      "name": "Send Job Report via Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2080,
        16
      ],
      "parameters": {
        "text": "={{ $json.content[0].text }}",
        "chatId": "={{ $('When Telegram Message Received').item.json.message.chat.id }}",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    }
  ],
  "active": true,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "55c2829e-957b-489e-bca5-854ea9d59088",
  "connections": {
    "Set Config Variables": {
      "main": [
        [
          {
            "node": "Check Telegram User ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse JSON for Sheets": {
      "main": [
        [
          {
            "node": "Append Job Data to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Message Type": {
      "main": [
        [
          {
            "node": "Read All Jobs from Sheets",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Claude Extract Job Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Telegram User ID": {
      "main": [
        [
          {
            "node": "Route by Message Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude Extract Job Info": {
      "main": [
        [
          {
            "node": "Parse JSON for Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Job Data to Sheet": {
      "main": [
        [
          {
            "node": "Send Telegram Log Confirmation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read All Jobs from Sheets": {
      "main": [
        [
          {
            "node": "Claude Analyze Job Patterns",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude Analyze Job Patterns": {
      "main": [
        [
          {
            "node": "Send Job Report via Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Telegram Message Received": {
      "main": [
        [
          {
            "node": "Set Config Variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}