{
  "id": "xpHd9hGWINvRvc9u",
  "name": "Job_ads_signals_final",
  "tags": [],
  "nodes": [
    {
      "id": "265d1925-158c-42f3-92fe-2e2c877faa88",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        576,
        -240
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Job_ads_signals\n\n### How it works\n\n1. Trigger the workflow manually or on a weekly schedule.\n2. Fetch and filter data from Hubspot.\n3. Search for job ads and create snapshots for comparison.\n4. Perform snapshot cleanup and validate job ad existence.\n5. Update job ads table and process company updates.\n\n### Setup steps\n\n- [ ] Set up Hubspot credentials in the n8n workspace.\n- [ ] Configure ElasticSearch settings for job ad searches.\n- [ ] Add access credentials for the data table manipulation nodes.\n- [ ] Ensure triggers (manual/weekly) have the correct configuration.\n- [ ] Code nodes require any JavaScript variables pre-defined within the workflows.\n\n### Customization\n\nYou can adjust filter criteria and comparison logic based on company-specific needs. Data snapshots and validation mechanisms can be adapted to existing database structure."
      },
      "typeVersion": 1
    },
    {
      "id": "b3b427d3-f542-43f6-988e-a773c4f5dba6",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1136,
        224
      ],
      "parameters": {
        "color": 7,
        "height": 496,
        "content": "## Workflow triggers\n\nStart the workflow with scheduled or manual initiation."
      },
      "typeVersion": 1
    },
    {
      "id": "84ec5d68-2956-4de2-8725-2354f0c9e422",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1440,
        336
      ],
      "parameters": {
        "color": 7,
        "width": 592,
        "height": 288,
        "content": "## Initialize and fetch companies\n\nInitialize the run and get company data from Hubspot."
      },
      "typeVersion": 1
    },
    {
      "id": "de9866de-d854-47a5-aa20-7038fd0d7ae8",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2080,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 304,
        "content": "## Filter and deduplicate companies\n\nFilter company records retrieved from Hubspot."
      },
      "typeVersion": 1
    },
    {
      "id": "6af804dc-23dc-4ba8-bf09-12533dee36a7",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2544,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 336,
        "content": "## Loop through filtered companies\n\nProcess filtered company batches to extract domains and search for job ads."
      },
      "typeVersion": 1
    },
    {
      "id": "c7a45fc3-0a95-46d9-85bf-1723f45677c0",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2944,
        304
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 304,
        "content": "## Search and snapshot job ads\n\nSearch for job ads and create a snapshot of data."
      },
      "typeVersion": 1
    },
    {
      "id": "b6e1034b-d79b-4722-974b-f1da3e9363fd",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3104,
        640
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 368,
        "content": "## Snapshot comparison\n\nCompare job snapshots from current and previous runs."
      },
      "typeVersion": 1
    },
    {
      "id": "21ea4fae-8dff-43d3-a05f-64f1f2c7dfc5",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3568,
        368
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 272,
        "content": "## Snapshot cleanup\n\nCleanup and ensure snapshot consistency."
      },
      "typeVersion": 1
    },
    {
      "id": "d94ec6c2-157c-4c3a-beb5-c1a97ad871d1",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4016,
        368
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 272,
        "content": "## Job existence check\n\nCheck which job ads exist and update the actual snapshot."
      },
      "typeVersion": 1
    },
    {
      "id": "30bddf8b-355d-4035-abcd-9b716dab6e06",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4608,
        352
      ],
      "parameters": {
        "color": 7,
        "width": 608,
        "height": 304,
        "content": "## Update job ads table\n\nUpdate job ads table based on checked existence and validate removals."
      },
      "typeVersion": 1
    },
    {
      "id": "8ac82212-78f8-4b5f-a165-fc1d70f9d43f",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3776,
        688
      ],
      "parameters": {
        "color": 7,
        "width": 832,
        "height": 272,
        "content": "## Validate and remove jobs\n\nValidate disappeared jobs and remove them from the database."
      },
      "typeVersion": 1
    },
    {
      "id": "e4401c6d-f533-47b4-a15f-8f6bf4cf99c1",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5248,
        368
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 272,
        "content": "## Prepare for company updates\n\nQuery rows and process data for company update actions."
      },
      "typeVersion": 1
    },
    {
      "id": "3b7faa8d-85a8-457c-8862-bb1400ec06d2",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5984,
        -192
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 432,
        "content": "## Compare company lists\n\nCompare old and new company lists to identify updates."
      },
      "typeVersion": 1
    },
    {
      "id": "7d671b01-466a-411d-8aad-353bda398703",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        6320,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 592,
        "height": 720,
        "content": "## Process company updates\n\nPerform updates for companies with new data or removed postings."
      },
      "typeVersion": 1
    },
    {
      "id": "02cdc8e6-50ff-416c-891d-a2aa0a6d9b1e",
      "name": "When Monday at 9am",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1184,
        384
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 9 * * 1"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b9163e4f-64fe-4400-aa13-aedc299f606e",
      "name": "Initialize Workflow",
      "type": "n8n-nodes-base.code",
      "position": [
        1488,
        464
      ],
      "parameters": {
        "jsCode": "const now = new Date().toISOString();\nconst runId = `cs-monitor-${now}`;\nreturn [{ json: { run_id: runId, run_started_at: now } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "6d8034c0-ae75-4f90-bd8f-5dd63bf5ff82",
      "name": "Search Jobs via Elasticsearch",
      "type": "n8n-nodes-coresignal-api.coresignal",
      "position": [
        2992,
        432
      ],
      "parameters": {
        "esQuery": "={\n  \"query\": {\n    \"bool\": {\n      \"must\": [\n        {\n          \"bool\": {\n            \"should\": [\n              {\n                \"bool\": {\n                  \"should\": [\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"customer support\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer  Support\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Support.\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"\uace0\uac1d\uc9c0\uc6d0\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customersupport\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"\u5ba2\u6237\u652f\u6301\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Kundst\u00f6d\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Kundensupport\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customers Support\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Service Support\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Kundesupport\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Soporte Al Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Kundsupport\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"\u062f\u0639\u0645 \u0627\u0644\u0639\u0645\u0644\u0627\u0621\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Services Support\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"M\u00fc\u015fteri Destek\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Kunden-Support\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Support Service\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Support Customer Service\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Care Support\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"M\u00fc\u015fteri Deste\u011fi\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Aten\u00e7\u00e3o Ao Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atencion Cliente\"\n                      }\n                    }\n                  ],\n                  \"minimum_should_match\": 1\n                }\n              },\n              {\n                \"bool\": {\n                  \"should\": [\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"customer service\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customerservice\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Service.\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer  Service\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Kundeservice\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Kundenservice\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atenci\u00f3n Al Cliente.\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"\u0e1a\u0e23\u0e34\u0e01\u0e32\u0e23\u0e25\u0e39\u0e01\u0e04\u0e49\u0e32\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Kundtj\u00e4nst\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Kundservice\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Asiakaspalvelu\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atenci\u00f3n Al Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atenci\u00f3n  Al Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atenci\u00f3n Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Kundendienst\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atenci\u00f3n Al Clientes\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"\u5ba2\u6236\u670d\u52d9\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Servicio Al Cliente.\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atenci\u00f3n Clientes\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customers Service\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Servicio Al Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"\u05e9\u05d9\u05e8\u05d5\u05ea \u05dc\u05e7\u05d5\u05d7\u05d5\u05ea\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Servis\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atendimento Ao Cliente.\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atenci\u00f3n A Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atendimento Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atendimento Ao Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atenci\u00f3n A Clientes.\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer-Service\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"\u062e\u062f\u0645\u0629 \u0627\u0644\u0639\u0645\u0644\u0627\u0621\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customerservices\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atenci\u00f3n De Clientes\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Servicio Atenci\u00f3n Al Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atenci\u00f3n A Clientes\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Pelayanan Pelanggan\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Servise\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"M\u00fc\u015fteri Hizmetleri\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Servi\u00e7o Ao Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customercare\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Servicio De Atenci\u00f3n Al Cliente.\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Servicio Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atencion Al Cliente.\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atencion  Al Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atencion Al Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atendimento A Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Servicio Atencion Al Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Servicio De Atenci\u00f3n Al Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Servcio Al Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Coustomer Service\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer  Services\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Services\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atencion Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Servicio Clientes\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Aten\u00e7\u00e3o Ao Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Atencion Al Clientes\"\n                      }\n                    }\n                  ],\n                  \"minimum_should_match\": 1\n                }\n              },\n              {\n                \"bool\": {\n                  \"should\": [\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"support representative\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Support Rep\"\n                      }\n                    }\n                  ],\n                  \"minimum_should_match\": 1\n                }\n              },\n              {\n                \"bool\": {\n                  \"should\": [\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"customer representative\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer  Representative\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Rep.\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customers Representative\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Rep\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Representante De Cliente\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Representive\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Representante De Clientes\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"Customer Representitive\"\n                      }\n                    },\n                    {\n                      \"match_phrase\": {\n                        \"title\": \"\u5ba2\u670d\u4ee3\u8868\"\n                      }\n                    }\n                  ],\n                  \"minimum_should_match\": 1\n                }\n              }\n            ],\n            \"minimum_should_match\": 1\n          }\n        },\n        {\n          \"terms\": {\n            \"company_domain\": \n       {{ $json.domains.toJsonString() }}\n          }\n        }\n      ],\n      \"filter\": [\n        {\n          \"term\": {\n            \"status\": 1\n          }\n        }\n      ]\n    }\n  },\n  \"sort\": [\n    \"company_employees_count\"\n  ]\n}",
        "resource": "job",
        "operation": "search_es_dsl"
      },
      "credentials": {
        "coresignalApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "b3215034-faf9-403a-af91-b63b98fc04d2",
      "name": "Fetch Job Details by ID",
      "type": "n8n-nodes-coresignal-api.coresignal",
      "onError": "continueRegularOutput",
      "position": [
        4256,
        480
      ],
      "parameters": {
        "jobId": "={{ $json.job_id }}",
        "resource": "job"
      },
      "credentials": {
        "coresignalApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "c07057dc-708a-45c6-ba4f-2d6a12fecf67",
      "name": "Wait 10 Seconds",
      "type": "n8n-nodes-base.wait",
      "position": [
        5072,
        480
      ],
      "parameters": {
        "amount": 10
      },
      "typeVersion": 1.1
    },
    {
      "id": "cf2cb405-df7c-41e6-82c1-ed71f4e83942",
      "name": "Retrieve Previous Run IDs",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        2944,
        832
      ],
      "parameters": {
        "matchType": "allConditions",
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "TFbKeONV0qpw1fGs",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/TFbKeONV0qpw1fGs",
          "cachedResultName": "previous snapshot"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "332e411d-0301-457c-bad1-0e778b2cdb32",
      "name": "Retrieve Company List Data",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        3440,
        -128
      ],
      "parameters": {
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "UFWhZZy4KFSac27Q",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/UFWhZZy4KFSac27Q",
          "cachedResultName": "company_data"
        }
      },
      "executeOnce": true,
      "typeVersion": 1.1
    },
    {
      "id": "2e162cfc-5a53-40de-ad5a-90d5d1607b7d",
      "name": "Create Current Snapshot",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        3232,
        432
      ],
      "parameters": {
        "columns": {
          "value": {
            "job_id": "={{ $json }}"
          },
          "schema": [
            {
              "id": "job_id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "job_id",
              "defaultMatch": false
            },
            {
              "id": "domain",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "domain",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "gOVbwnox6HLZa5yz",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/gOVbwnox6HLZa5yz",
          "cachedResultName": "current snapshot"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "e84fead8-3d70-430c-86db-1157b2f2cd65",
      "name": "Compare Previous and Current IDs",
      "type": "n8n-nodes-base.compareDatasets",
      "position": [
        3360,
        768
      ],
      "parameters": {
        "options": {
          "skipFields": "createdAt, updatedAt, id"
        },
        "mergeByFields": {
          "values": [
            {
              "field1": "job_id",
              "field2": "job_id"
            }
          ]
        }
      },
      "executeOnce": false,
      "typeVersion": 2.3
    },
    {
      "id": "ed41de19-c3db-46f7-8663-7705c2bdae67",
      "name": "Clear Current Snapshot",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        3616,
        480
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "job_id",
              "condition": "isNotEmpty"
            }
          ]
        },
        "options": {
          "dryRun": false
        },
        "operation": "deleteRows",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "gOVbwnox6HLZa5yz",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/gOVbwnox6HLZa5yz",
          "cachedResultName": "current snapshot"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "ac048b69-d923-4704-94a2-df9759bac3eb",
      "name": "Check Snapshot IDs in Database",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        3824,
        480
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "job_id",
              "keyValue": "={{ $json.job_id }}"
            }
          ]
        },
        "operation": "rowNotExists",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "TFbKeONV0qpw1fGs",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/TFbKeONV0qpw1fGs",
          "cachedResultName": "previous snapshot"
        }
      },
      "typeVersion": 1.1,
      "alwaysOutputData": true
    },
    {
      "id": "b51375c5-6dd6-4b17-a430-3e747e1cd0e6",
      "name": "Update Active Snapshot",
      "type": "n8n-nodes-base.dataTable",
      "onError": "continueRegularOutput",
      "position": [
        4064,
        480
      ],
      "parameters": {
        "columns": {
          "value": {
            "job_id": "={{ $json.job_id }}"
          },
          "schema": [
            {
              "id": "job_id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "job_id",
              "defaultMatch": false
            },
            {
              "id": "domain",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "domain",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "TFbKeONV0qpw1fGs",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/TFbKeONV0qpw1fGs",
          "cachedResultName": "previous snapshot"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "9e8fde7c-562c-4e52-a144-f9c77bdc5065",
      "name": "Check Job Ad Presence",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        4432,
        480
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "job_id",
              "keyValue": "={{ $json.id }}"
            }
          ]
        },
        "operation": "rowNotExists",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "E1tGnBBo2NJauMUs",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/E1tGnBBo2NJauMUs",
          "cachedResultName": "job_data_table"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "9bb7b78a-aef9-4adb-b80f-baaa26e5e912",
      "name": "Update Job Ads Table",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        4656,
        480
      ],
      "parameters": {
        "columns": {
          "value": {
            "url": "={{ $json.external_url }}",
            "title": "={{ $json.title }}",
            "job_id": "={{ $json.id }}",
            "source": "={{ $json.job_sources[0].source }}",
            "status": "={{ $json.status }}",
            "location": "={{ $json.location }}",
            "posted_at": "={{ $json.created_at }}",
            "description": "={{ $json.description }}",
            "company_domain": "={{ $json.company_domain }}",
            "applicants_count": "={{ $json.applicants_count }}"
          },
          "schema": [
            {
              "id": "job_id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "job_id",
              "defaultMatch": false
            },
            {
              "id": "company_domain",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "company_domain",
              "defaultMatch": false
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "status",
              "defaultMatch": false
            },
            {
              "id": "posted_at",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "posted_at",
              "defaultMatch": false
            },
            {
              "id": "source",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "source",
              "defaultMatch": false
            },
            {
              "id": "url",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "url",
              "defaultMatch": false
            },
            {
              "id": "title",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "title",
              "defaultMatch": false
            },
            {
              "id": "description",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "description",
              "defaultMatch": false
            },
            {
              "id": "location",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "location",
              "defaultMatch": false
            },
            {
              "id": "applicants_count",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "applicants_count",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "E1tGnBBo2NJauMUs",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/E1tGnBBo2NJauMUs",
          "cachedResultName": "job_data_table"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "ea618bcf-d3ab-4d72-a372-19b6287ee57d",
      "name": "Remove Obsolete Job Ads",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        3824,
        800
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "job_id",
              "keyValue": "={{ $json.job_id }}"
            }
          ]
        },
        "options": {},
        "operation": "deleteRows",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "TFbKeONV0qpw1fGs",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/TFbKeONV0qpw1fGs",
          "cachedResultName": "previous snapshot"
        }
      },
      "executeOnce": false,
      "typeVersion": 1.1
    },
    {
      "id": "b74fb3c7-527a-40a8-8ac3-5b3fef29566b",
      "name": "Validate Job Ad History",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        4064,
        800
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "job_id",
              "keyValue": "={{ $json.job_id }}"
            }
          ]
        },
        "operation": "rowExists",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "E1tGnBBo2NJauMUs",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/E1tGnBBo2NJauMUs",
          "cachedResultName": "job_data_table"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "bc21c4c3-bdf1-4f41-bca3-aace0ab4ac49",
      "name": "Remove Job Ads from Table",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        4464,
        800
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "job_id"
            }
          ]
        },
        "options": {},
        "operation": "deleteRows",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "E1tGnBBo2NJauMUs",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/E1tGnBBo2NJauMUs",
          "cachedResultName": "job_data_table"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "469daf0a-f444-4fcb-90be-386e8e0c30f9",
      "name": "Retrieve Table Rows",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        5296,
        480
      ],
      "parameters": {
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "E1tGnBBo2NJauMUs",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/E1tGnBBo2NJauMUs",
          "cachedResultName": "job_data_table"
        }
      },
      "executeOnce": true,
      "typeVersion": 1.1
    },
    {
      "id": "045845f6-69be-4393-acf2-b0963ebc36e8",
      "name": "Process Table Rows",
      "type": "n8n-nodes-base.code",
      "position": [
        5488,
        480
      ],
      "parameters": {
        "jsCode": "// n8n Code node\n// Mode: Run Once for All Items\n//\n// Input: one item per ACTIVE job ad\n// Output: one item per company_domain\n//\n// Expected input fields per job (best effort; code is defensive):\n// - company_domain\n// - title\n// - external_url OR url\n// - date_posted OR created_at OR posted_at\n// - location OR city / state / country\n// - job_sources (array with source fields) OR source\n// - applicants_count (optional)\n//\n// Notes:\n// - No historical storage is used.\n// - Since this is an active digest, job_active_cs_listings_total = number of active rows in input for that company.\n// - \"Hottest job\" logic:\n//    1) highest applicants_count, if at least one job for the company has a numeric applicants_count\n//    2) otherwise most recent posted date\n// - jobs_last_7_days / jobs_last_30_days are based on the best available posted date field.\n\nfunction normalizeDomain(value) {\n  return String(value || '')\n    .toLowerCase()\n    .trim()\n    .replace(/^www\\./, '');\n}\n\nfunction toDate(value) {\n  if (!value) return null;\n  const d = new Date(value);\n  return Number.isNaN(d.getTime()) ? null : d;\n}\n\nfunction toIsoDate(value) {\n  const d = toDate(value);\n  return d ? d.toISOString() : '';\n}\n\nfunction getPostedDate(job) {\n  return (\n    job.date_posted ||\n    job.posted_at ||\n    job.created_at ||\n    job.createdAt ||\n    job.updated_at ||\n    ''\n  );\n}\n\nfunction parseApplicantsCount(value) {\n  if (value === null || value === undefined || value === '') return null;\n\n  // already numeric\n  if (typeof value === 'number' && Number.isFinite(value)) return value;\n\n  const str = String(value).trim().toLowerCase();\n\n  // common patterns:\n  // \"23\", \"23 applicants\", \"23+ applicants\", \"100+\", \"over 200 applicants\"\n  const match = str.match(/(\\d+(?:[.,]\\d+)?)/);\n  if (!match) return null;\n\n  const num = Number(match[1].replace(',', '.'));\n  return Number.isFinite(num) ? num : null;\n}\n\nfunction buildLocation(job) {\n  if (job.location && String(job.location).trim()) {\n    return String(job.location).trim();\n  }\n\n  const parts = [\n    job.city,\n    job.state,\n    job.country\n  ]\n    .map(v => String(v || '').trim())\n    .filter(Boolean);\n\n  return parts.join(', ');\n}\n\nfunction extractSources(job) {\n  const out = new Set();\n\n  // single source field\n  if (job.source) {\n    out.add(String(job.source).trim());\n  }\n\n  // nested job_sources array\n  if (Array.isArray(job.job_sources)) {\n    for (const s of job.job_sources) {\n      if (!s) continue;\n      if (s.source) out.add(String(s.source).trim());\n      else if (s.name) out.add(String(s.name).trim());\n    }\n  }\n\n  return [...out].filter(Boolean);\n}\n\nfunction choosePrimaryLocation(locationCounts) {\n  let bestLocation = '';\n  let bestCount = -1;\n\n  for (const [location, count] of Object.entries(locationCounts)) {\n    if (count > bestCount) {\n      bestLocation = location;\n      bestCount = count;\n    }\n  }\n\n  return bestLocation;\n}\n\nfunction chooseHottestJob(jobs) {\n  // First preference: highest applicants_count if present\n  const jobsWithApplicants = jobs\n    .map(job => ({\n      job,\n      applicants_count_num: parseApplicantsCount(job.applicants_count),\n      postedDate: toDate(getPostedDate(job)),\n    }))\n    .filter(x => x.applicants_count_num !== null);\n\n  if (jobsWithApplicants.length > 0) {\n    jobsWithApplicants.sort((a, b) => {\n      if (b.applicants_count_num !== a.applicants_count_num) {\n        return b.applicants_count_num - a.applicants_count_num;\n      }\n\n      const aTime = a.postedDate ? a.postedDate.getTime() : 0;\n      const bTime = b.postedDate ? b.postedDate.getTime() : 0;\n      return bTime - aTime;\n    });\n\n    return jobsWithApplicants[0].job;\n  }\n\n  // Fallback: most recent posted date\n  const sortedByDate = [...jobs].sort((a, b) => {\n    const aTime = toDate(getPostedDate(a))?.getTime() || 0;\n    const bTime = toDate(getPostedDate(b))?.getTime() || 0;\n    return bTime - aTime;\n  });\n\n  return sortedByDate[0] || null;\n}\n\nconst inputJobs = $input.all().map(i => i.json);\nconst now = new Date();\nconst ms7 = 7 * 24 * 60 * 60 * 1000;\nconst ms30 = 30 * 24 * 60 * 60 * 1000;\n\nconst companies = {};\n\n// Group jobs by company_domain\nfor (const job of inputJobs) {\n  const companyDomain = normalizeDomain(job.company_domain);\n\n  if (!companyDomain) continue;\n\n  if (!companies[companyDomain]) {\n    companies[companyDomain] = {\n      company_domain: companyDomain,\n      jobs: [],\n      locationCounts: {},\n      sourceSet: new Set(),\n      latestPostedDate: null,\n      earliestPostedDate: null,\n    };\n  }\n\n  const company = companies[companyDomain];\n  company.jobs.push(job);\n\n  // location aggregation\n  const location = buildLocation(job);\n  if (location) {\n    company.locationCounts[location] = (company.locationCounts[location] || 0) + 1;\n  }\n\n  // source aggregation\n  const sources = extractSources(job);\n  for (const source of sources) {\n    company.sourceSet.add(source);\n  }\n\n  // posted date range\n  const postedDate = toDate(getPostedDate(job));\n  if (postedDate) {\n    if (!company.latestPostedDate || postedDate > company.latestPostedDate) {\n      company.latestPostedDate = postedDate;\n    }\n    if (!company.earliestPostedDate || postedDate < company.earliestPostedDate) {\n      company.earliestPostedDate = postedDate;\n    }\n  }\n}\n\n// Build company digest rows\nconst result = [];\n\nfor (const companyDomain of Object.keys(companies)) {\n  const company = companies[companyDomain];\n  const jobs = company.jobs;\n\n  const jobsLast7Days = jobs.filter(job => {\n    const posted = toDate(getPostedDate(job));\n    return posted ? (now - posted) <= ms7 : false;\n  }).length;\n\n  const jobsLast30Days = jobs.filter(job => {\n    const posted = toDate(getPostedDate(job));\n    return posted ? (now - posted) <= ms30 : false;\n  }).length;\n\n  const hottestJob = chooseHottestJob(jobs) || {};\n  const hottestPosted = getPostedDate(hottestJob);\n\n  result.push({\n    json: {\n      company_domain: company.company_domain,\n\n      job_active_cs_listings_total: jobs.length,\n      jobs_last_7_days: jobsLast7Days,\n      jobs_last_30_days: jobsLast30Days,\n\n      job_signal_detected: jobs.length > 0,\n\n      top_job_title: hottestJob.title || '',\n      top_job_url: hottestJob.external_url || hottestJob.url || '',\n      top_job_posted_at: toIsoDate(hottestPosted),\n\n      top_job_applicants_count: parseApplicantsCount(hottestJob.applicants_count),\n      top_job_location: buildLocation(hottestJob),\n\n      primary_location: choosePrimaryLocation(company.locationCounts),\n      locations_count: Object.keys(company.locationCounts).length,\n\n      sources: [...company.sourceSet],\n      sources_count: company.sourceSet.size,\n\n      last_job_posted_at: company.latestPostedDate ? company.latestPostedDate.toISOString() : '',\n      first_job_seen_at: company.earliestPostedDate ? company.earliestPostedDate.toISOString() : '',\n    }\n  });\n}\n\n// Optional: sort companies by active listings desc, then recent activity desc\nresult.sort((a, b) => {\n  if (b.json.job_active_cs_listings_total !== a.json.job_active_cs_listings_total) {\n    return b.json.job_active_cs_listings_total - a.json.job_active_cs_listings_total;\n  }\n  return b.json.jobs_last_7_days - a.json.jobs_last_7_days;\n});\n\nreturn result;"
      },
      "typeVersion": 2
    },
    {
      "id": "04cc241a-8761-422d-a789-a527093922a3",
      "name": "Upsert Company Data",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        5664,
        480
      ],
      "parameters": {
        "columns": {
          "value": {
            "top_job_url": "={{ $json.top_job_url }}",
            "top_job_title": "={{ $json.top_job_title }}",
            "company_domain": "={{ $json.company_domain }}",
            "locations_count": "={{ $json.locations_count }}",
            "jobs_last_7_days": "={{ $json.jobs_last_7_days }}",
            "primary_location": "={{ $json.primary_location }}",
            "top_job_location": "={{ $json.top_job_location }}",
            "first_job_seen_at": "={{ $json.first_job_seen_at }}",
            "jobs_last_30_days": "={{ $json.jobs_last_30_days }}",
            "top_job_posted_at": "={{ $json.top_job_posted_at }}",
            "last_job_posted_at": "={{ $json.last_job_posted_at }}",
            "job_signal_detected": "={{ $json.job_signal_detected }}",
            "top_job_applicants_count": "={{ $json.top_job_applicants_count }}",
            "job_active_cs_listings_total": "={{ $json.job_active_cs_listings_total }}"
          },
          "schema": [
            {
              "id": "company_domain",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "company_domain",
              "defaultMatch": false
            },
            {
              "id": "job_active_cs_listings_total",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "job_active_cs_listings_total",
              "defaultMatch": false
            },
            {
              "id": "jobs_last_7_days",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "jobs_last_7_days",
              "defaultMatch": false
            },
            {
              "id": "jobs_last_30_days",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "jobs_last_30_days",
              "defaultMatch": false
            },
            {
              "id": "job_signal_detected",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "job_signal_detected",
              "defaultMatch": false
            },
            {
              "id": "top_job_title",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "top_job_title",
              "defaultMatch": false
            },
            {
              "id": "top_job_url",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "top_job_url",
              "defaultMatch": false
            },
            {
              "id": "top_job_posted_at",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "top_job_posted_at",
              "defaultMatch": false
            },
            {
              "id": "top_job_applicants_count",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "top_job_applicants_count",
              "defaultMatch": false
            },
            {
              "id": "top_job_location",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "top_job_location",
              "defaultMatch": false
            },
            {
              "id": "primary_location",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "primary_location",
              "defaultMatch": false
            },
            {
              "id": "locations_count",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "locations_count",
              "defaultMatch": false
            },
            {
              "id": "last_job_posted_at",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "last_job_posted_at",
              "defaultMatch": false
            },
            {
              "id": "first_job_seen_at",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "first_job_seen_at",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "filters": {
          "conditions": [
            {
              "keyName": "company_domain",
              "keyValue": "={{ $json.company_domain }}"
            }
          ]
        },
        "options": {},
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "UFWhZZy4KFSac27Q",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/UFWhZZy4KFSac27Q",
          "cachedResultName": "company_data"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "899e9aeb-8131-4bcc-ab47-c47d3e1e7b43",
      "name": "Compare Company Lists",
      "type": "n8n-nodes-base.compareDatasets",
      "position": [
        6048,
        -16
      ],
      "parameters": {
        "options": {
          "skipFields": "updatedAt, createdAt, id"
        },
        "mergeByFields": {
          "values": [
            {
              "field1": "company_domain",
              "field2": "company_domain"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "be2b135f-6fb3-4e73-9629-bde08c8db65b",
      "name": "Identify New Companies",
      "type": "n8n-nodes-base.code",
      "position": [
        6576,
        512
      ],
      "parameters": {
        "jsCode": "// n8n Code node\n// Mode: Run Once for All Items\n//\n// Input: rows that exist only in B\n// HubSpot source node: Filter1\n\nfunction normalizeDomain(value) {\n  return String(value || '')\n    .toLowerCase()\n    .trim()\n    .replace(/^www\\./, '');\n}\n\n// Build HubSpot domain -> ids map\nconst hsItems = $items('Filter Active Companies', 0, 0) || [];\nconst hubspotByDomain = {};\n\nfor (const item of hsItems) {\n  const row = item.json || {};\n  const domain = normalizeDomain(row.properties.website.versions[0].value);\n  if (!domain) continue;\n\n  hubspotByDomain[domain] = {\n    company_id: row.companyId ?? null,\n    portal_id: row.portalId ?? null,\n  };\n}\n\n// Join current input with HubSpot map\nconst out = [];\n\nfor (const item of $input.all()) {\n  const row = { ...(item.json || {}) };\n  const domain = normalizeDomain(row.company_domain);\n\n  const hs = hubspotByDomain[domain] || {};\n\n  row.company_domain = domain;\n  row.company_id = hs.company_id ?? null;\n  row.portal_id = hs.portal_id ?? null;\n\n  out.push({ json: row });\n}\n\nreturn out;"
      },
      "typeVersion": 2
    },
    {
      "id": "6736a184-d0cc-4e84-8ff7-b58b5e9bd77e",
      "name": "Identify Updated Companies",
      "type": "n8n-nodes-base.code",
      "position": [
        6576,
        320
      ],
      "parameters": {
        "jsCode": "// n8n Code node\n// Mode: Run Once for All Items\n//\n// Input: comparison node \"Different\" output items\n// Looks up HubSpot company ID from node: Filter1\n\nfunction normalizeDomain(value) {\n  return String(value || '')\n    .toLowerCase()\n    .trim()\n    .replace(/^www\\./, '');\n}\n\nfunction toNumber(value) {\n  if (value === null || value === undefined || value === '') return null;\n  const num = Number(value);\n  return Number.isFinite(num) ? num : null;\n}\n\nfunction toBoolean(value) {\n  if (typeof value === 'boolean') return value;\n  if (value === 1 || value === '1') return true;\n  if (value === 0 || value === '0') return false;\n  if (typeof value === 'string') {\n    const v = value.toLowerCase().trim();\n    if (v === 'true') return true;\n    if (v === 'false') return false;\n  }\n  return value;\n}\n\n// --- Build HubSpot domain -> companyId map from Filter1 ---\nconst hsItems = $items('Filter Active Companies', 0, 0) || [];\nconst hubspotByDomain = {};\n\nfor (const item of hsItems) {\n  const row = item.json || {};\n  const domain = normalizeDomain(row.properties?.domain?.value);\n  if (!domain) continue;\n\n  hubspotByDomain[domain] = {\n    company_id: row.companyId ?? null,\n    portal_id: row.portalId ?? null,\n  };\n}\n\n// --- Normalize comparison rows ---\nconst inputItems = $input.all();\nconst output = [];\n\nfor (const item of inputItems) {\n  const row = item.json || {};\n  const keys = row.keys || {};\n  const same = row.same || {};\n  const different = row.different || {};\n\n  // Start from unchanged values\n  const normalized = { ...same };\n\n  // Overlay changed fields with inputB values\n  for (const [field, diffValue] of Object.entries(different)) {\n    if (diffValue && typeof diffValue === 'object' && 'inputB' in diffValue) {\n      normalized[field] = diffValue.inputB;\n    }\n  }\n\n  // Ensure company_domain exists\n  if (!normalized.company_domain && keys.company_domain) {\n    normalized.company_domain = keys.company_domain;\n  }\n\n  normalized.company_domain = normalizeDomain(normalized.company_domain);\n\n  // Attach HubSpot company ID\n  const hs = hubspotByDomain[normalized.company_domain] || {};\n  normalized.company_id = hs.company_id ?? null;\n  normalized.portal_id = hs.portal_id ?? null;\n\n  // --- Change analysis ---\n  // Current (B) and previous (A) active counts\n  let currentActive = toNumber(normalized.job_active_cs_listings_total);\n  let previousActive = currentActive;\n\n  if (\n    different.job_active_cs_listings_total &&\n    typeof different.job_active_cs_listings_total === 'object'\n  ) {\n    previousActive = toNumber(different.job_active_cs_listings_total.inputA);\n    currentActive = toNumber(different.job_active_cs_listings_total.inputB);\n  }\n\n  let delta = null;\n  let trend = 'unchanged';\n  let rose = false;\n  let fell = false;\n\n  if (previousActive !== null && currentActive !== null) {\n    delta = currentActive - previousActive;\n    if (delta > 0) {\n      trend = 'rose';\n      rose = true;\n    } else if (delta < 0) {\n      trend = 'fell';\n      fell = true;\n    }\n  }\n\n  // Top title change\n  let previousTopTitle = normalized.top_job_title || '';\n  let currentTopTitle = normalized.top_job_title || '';\n  let topTitleChanged = false;\n\n  if (different.top_job_title && typeof different.top_job_title === 'object') {\n    previousTopTitle = different.top_job_title.inputA || '';\n    currentTopTitle = different.top_job_title.inputB || '';\n    topTitleChanged = previousTopTitle !== currentTopTitle;\n    normalized.top_job_title = currentTopTitle;\n  }\n\n  // Normalize common field types\n  normalized.job_signal_detected = toBoolean(normalized.job_signal_detected);\n\n  // Add derived fields\n  normalized.previous_job_active_cs_listings_total = previousActive;\n  normalized.job_active_cs_listings_delta = delta;\n  normalized.job_active_cs_listings_trend = trend;\n  normalized.job_ads_rose = rose;\n  normalized.job_ads_fell = fell;\n\n  normalized.previous_top_job_title = previousTopTitle;\n  normalized.top_title_changed = topTitleChanged;\n\n  // Optional: mark this as an update row\n  normalized.needs_update = true;\n  normalized.update_type = 'comparison_diff';\n\n  output.push({ json: normalized });\n}\n\nreturn output;"
      },
      "typeVersion": 2
    },
    {
      "id": "116a4eb9-5788-4585-adca-cc86a2ea619c",
      "name": "Identify Companies Ending CS Ads",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        6368,
        80
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "company_domain",
              "keyValue": "={{ $json.company_domain }}"
            }
          ]
        },
        "options": {},
        "operation": "deleteRows",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "UFWhZZy4KFSac27Q",
          "cachedResultUrl": "/projects/d8siYNB21z0O177I/datatables/UFWhZZy4KFSac27Q",
          "cachedResultName": "company_data"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "5891c61d-f56f-450f-aaf9-cc06ccbafe7e",
      "name": "Process Ending CS Ads",
      "type": "n8n-nodes-base.code",
      "position": [
        6576,
        80
      ],
      "parameters": {
        "jsCode": "// n8n Code node\n// Mode: Run Once for All Items\n//\n// Input: companies that exist only in B-side workflow branch\n// HubSpot source node: Filter1\n//\n// Output:\n// - joins HubSpot company_id / portal_id by domain\n// - resets job-related fields to 0 / false / empty string\n\nfunction normalizeDomain(value) {\n  return String(value || '')\n    .toLowerCase()\n    .trim()\n    .replace(/^www\\./, '');\n}\n\n// Build HubSpot domain -> ids map\nconst hsItems = $items('Filter Active Companies', 0, 0) || [];\nconst hubspotByDomain = {};\n\nfor (const item of hsItems) {\n  const row = item.json || {};\n  const domain = normalizeDomain(row.properties?.domain?.value);\n  if (!domain) continue;\n\n  hubspotByDomain[domain] = {\n    company_id: row.companyId ?? null,\n    portal_id: row.portalId ?? null,\n  };\n}\n\nconst out = [];\n\nfor (const item of $input.all()) {\n  const row = item.json || {};\n  const domain = normalizeDomain(row.company_domain);\n  const hs = hubspotByDomain[domain] || {};\n\n  out.push({\n    json: {\n      company_domain: domain,\n      company_id: hs.company_id ?? null,\n      portal_id: hs.portal_id ?? null,\n\n      job_active_cs_listings_total: 0,\n      jobs_last_7_days: 0,\n      jobs_last_30_days: 0,\n      job_signal_detected: 0,\n\n      top_job_title: '',\n      top_job_url: '',\n      top_job_posted_at: '',\n      top_job_applicants_count: 0,\n      top_job_location: '',\n\n      primary_location: '',\n      locations_count: 0,\n\n      last_job_posted_at: '',\n      first_job_seen_at: '',\n\n      // keep row identifiers if you need them for update logic\n      id: row.id ?? null,\n      createdAt: row.createdAt ?? null,\n      updatedAt: row.updatedAt ?? null\n    }\n  });\n}\n\nreturn out;"
      },
      "typeVersion": 2
    },
    {
      "id": "2a97d269-aa31-4eab-9fc2-096311bc3d1e",
      "name": "Manual Workflow Start",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        1184,
        560
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "e0cc6857-5771-47e9-82bf-42d893aa37e8",
      "name": "Fetch Company List",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        1888,
        448
      ],
      "parameters": {
        "options": {
          "propertiesCollection": {
            "propertiesValues": {
              "properties": [
                "website"
              ]
            }
          }
        },
        "resource": "company",
        "operation": "getAll",
        "returnAll": true,
        "authentication": "oAuth2"
      },
      "credentials": {
        "hubspotOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "0407912c-301f-486a-8a77-c20ff44749cc",
      "name": "Update Company Status",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        6768,
        80
      ],
      "parameters": {
        "resource": "company",
        "companyId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.company_id }}"
        },
        "operation": "update",
        "updateFields": {
          "customPropertiesUi": {
            "customPropertiesValues": [
              {
                "value": "={{ $json.job_active_cs_listings_total }}",
                "property": "job_active_listings_total"
              },
              {
                "value": "={{ $json.jobs_last_30_days }}",
                "property": "jobs_last_30_days"
              },
              {
                "value": "={{ $json.jobs_last_7_days }}",
                "property": "jobs_last_7_days"
              },
              {
                "value": "={{ $json.locations_count }}",
                "property": "locations_count"
              },
              {
                "value": "={{ $json.job_signal_detected }}",
                "property": "nr_of_job_signals"
              },
              {
                "value": "={{ $json.primary_location }}",
                "property": "primary_location"
              },
              {
                "value": "={{ $json.top_job_location }}",
                "property": "top_job_location"
              },
              {
                "value": "={{ $json.top_job_posted_at }}",
                "property": "top_job_posted_at"
              },
              {
                "value": "={{ $json.top_job_title }}",
                "property": "top_job_title"
              },
              {
                "value": "={{ $json.top_job_url }}",
                "property": "top_job_url"
              }
            ]
          }
        },
        "authentication": "oAuth2"
      },
      "credentials": {
        "hubspotOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "30468591-fab4-441a-a7ce-4b2a63ceb8a5",
      "name": "Extract Domains from Data",
      "type": "n8n-nodes-base.code",
      "position": [
        2768,
        480
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst domains = items.map((item) => item.json.properties.website.versions[0].value);\nreturn { json: { domains } };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "da2c82d2-138a-4a14-bc01-8e78cf5f815a",
      "name": "Loop Over Company Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        2592,
        448
      ],
      "parameters": {
        "options": {},
        "batchSize": 50
      },
      "typeVersion": 3
    },
    {
      "id": "dbfc2ad4-56e4-45ee-b0f8-cdeeb325fc94",
      "name": "Sticky Note14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3360,
        -336
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 432,
        "content": "## Fetch existing company list\n\nFetching existing company list to compare it with the newly generated one \n"
      },
      "typeVersion": 1
    },
    {
      "id": "2c69c948-0a51-4921-9500-cb66e457766d",
      "name": "Update Existing Company",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        6768,
        320
      ],
      "parameters": {
        "resource": "company",
        "companyId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.company_id }}"
        },
        "operation": "update",
        "updateFields": {
          "customPropertiesUi": {
            "customPropertiesValues": [
              {
                "value": "={{ $json.job_active_cs_listings_total }}",
                "property": "job_active_listings_total"
              },
              {
                "value": "={{ $json.jobs_last_30_days }}",
                "property": "jobs_last_30_days"
              },
              {
                "value": "={{ $json.jobs_last_7_days }}",
                "property": "jobs_last_7_days"
              },
              {
                "value": "={{ $json.locations_count }}",
                "property": "locations_count"
              },
              {
                "value": "={{ $json.job_signal_detected }}",
                "property": "nr_of_job_signals"
              },
              {
                "value": "={{ $json.primary_location }}",
                "property": "primary_location"
              },
              {
                "value": "={{ $json.top_job_location }}",
                "property": "top_job_location"
              },
              {
                "value": "={{ $json.top_job_posted_at }}",
                "property": "top_job_posted_at"
              },
              {
                "value": "={{ $json.top_job_title }}",
                "property": "top_job_title"
              },
              {
                "value": "={{ $json.top_job_url }}",
                "property": "top_job_url"
              }
            ]
          }
        },
        "authentication": "oAuth2"
      },
      "credentials": {
        "hubspotOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "7faf95d8-6078-40e6-a7e1-d1be6051d27c",
      "name": "Add new company",
      "type": "n8n-nodes-base.hubspot",
      "position": [
        6768,
        512
      ],
      "parameters": {
        "resource": "company",
        "companyId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.company_id }}"
        },
        "operation": "update",
        "updateFields": {
          "customPropertiesUi": {
            "customPropertiesValues": [
              {
                "value": "={{ $json.job_active_cs_listings_total }}",
                "property": "job_active_listings_total"
              },
              {
                "value": "={{ $json.jobs_last_30_days }}",
                "property": "jobs_last_30_days"
              },
              {
                "value": "={{ $json.jobs_last_7_days }}",
                "property": "jobs_last_7_days"
              },
              {
                "value": "={{ $json.locations_count }}",
                "property": "locations_count"
              },
              {
                "value": "={{ $json.job_signal_detected }}",
                "property": "nr_of_job_signals"
              },
              {
                "value": "={{ $json.primary_location }}",
                "property": "primary_location"
              },
              {
                "value": "={{ $json.top_job_location }}",
                "property": "top_job_location"
              },
              {
                "value": "={{ $json.top_job_posted_at }}",
                "property": "top_job_posted_at"
              },
              {
                "value": "={{ $json.top_job_title }}",
                "property": "top_job_title"
              },
              {
                "value": "={{ $json.top_job_url }}",
                "property": "top_job_url"
              }
            ]
          }
        },
        "authentication": "oAuth2"
      },
      "credentials": {
        "hubspotOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "67158540-e4cd-4dfa-b4c4-e9198ae4d2d7",
      "name": "Filter Active Companies",
      "type": "n8n-nodes-base.filter",
      "onError": "continueRegularOutput",
      "position": [
        2352,
        448
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "267f70d4-c61c-441c-bf67-0304437a57ee",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.properties.website.versions[0].value }}",
              "rightValue": 9
            }
          ]
        },
        "looseTypeValidation": true
      },
      "typeVersion": 2.3
    },
    {
      "id": "89c4a77c-3634-409f-b2a0-23ba83b14cc4",
      "name": "Remove Duplicates",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        2128,
        448
      ],
      "parameters": {
        "compare": "selectedFields",
        "options": {},
        "fieldsToCompare": "properties.website.value"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "15b325c1-81e0-472a-9eeb-43baef7f585e",
  "connections": {
    "Wait 10 Seconds": {
      "main": [
        [
          {
            "node": "Retrieve Table Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicates": {
      "main": [
        [
          {
            "node": "Filter Active Companies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Company List": {
      "main": [
        [
          {
            "node": "Remove Duplicates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Table Rows": {
      "main": [
        [
          {
            "node": "Upsert Company Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Monday at 9am": {
      "main": [
        [
          {
            "node": "Initialize Workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Initialize Workflow": {
      "main": [
        [
          {
            "node": "Retrieve Previous Run IDs",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Company List",
            "type": "main",
            "index": 0
          },
          {
            "node": "Retrieve Company List Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Table Rows": {
      "main": [
        [
          {
            "node": "Process Table Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upsert Company Data": {
      "main": [
        [
          {
            "node": "Compare Company Lists",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Update Job Ads Table": {
      "main": [
        [
          {
            "node": "Wait 10 Seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Job Ad Presence": {
      "main": [
        [
          {
            "node": "Update Job Ads Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare Company Lists": {
      "main": [
        [
          {
            "node": "Identify Companies Ending CS Ads",
            "type": "main",
            "index": 0
          }
        ],
        [],
        [
          {
            "node": "Identify Updated Companies",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Identify New Companies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Workflow Start": {
      "main": [
        [
          {
            "node": "Initialize Workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Ending CS Ads": {
      "main": [
        [
          {
            "node": "Update Company Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clear Current Snapshot": {
      "main": [
        [
          {
            "node": "Check Snapshot IDs in Database",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Identify New Companies": {
      "main": [
        [
          {
            "node": "Add new company",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Active Snapshot": {
      "main": [
        [
          {
            "node": "Fetch Job Details by ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Current Snapshot": {
      "main": [
        [
          {
            "node": "Compare Previous and Current IDs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Job Details by ID": {
      "main": [
        [
          {
            "node": "Check Job Ad Presence",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Active Companies": {
      "main": [
        [
          {
            "node": "Loop Over Company Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Company Items": {
      "main": [
        [
          {
            "node": "Search Jobs via Elasticsearch",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Extract Domains from Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Obsolete Job Ads": {
      "main": [
        [
          {
            "node": "Validate Job Ad History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Job Ad History": {
      "main": [
        [
          {
            "node": "Remove Job Ads from Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Domains from Data": {
      "main": [
        [
          {
            "node": "Loop Over Company Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Remove Job Ads from Table": {
      "main": [
        [
          {
            "node": "Wait 10 Seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Previous Run IDs": {
      "main": [
        [
          {
            "node": "Compare Previous and Current IDs",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Identify Updated Companies": {
      "main": [
        [
          {
            "node": "Update Existing Company",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Company List Data": {
      "main": [
        [
          {
            "node": "Compare Company Lists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Jobs via Elasticsearch": {
      "main": [
        [
          {
            "node": "Create Current Snapshot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Snapshot IDs in Database": {
      "main": [
        [
          {
            "node": "Update Active Snapshot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compare Previous and Current IDs": {
      "main": [
        [
          {
            "node": "Clear Current Snapshot",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Clear Current Snapshot",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Clear Current Snapshot",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Remove Obsolete Job Ads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Identify Companies Ending CS Ads": {
      "main": [
        [
          {
            "node": "Process Ending CS Ads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}