AutomationFlowsData & Sheets › Job.application_logger

Job.application_logger

job.application_logger. Uses httpRequest, postgres. Webhook trigger; 5 nodes.

Webhook trigger★★★★☆ complexity5 nodesHTTP RequestPostgres
Data & Sheets Trigger: Webhook Nodes: 5 Complexity: ★★★★☆ Added:

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

The workflow JSON

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

Download .json
{
  "name": "job.application_logger",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "ef639907-3887-45a9-a5db-5654ffab87a9",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -176,
        0
      ],
      "id": "11113675-c9dc-479c-b963-7a27cca79550",
      "name": "Extension Listener"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://enhancer-api:8000/enhance",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"url\": \"{{ $json.body.url }}\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        16,
        0
      ],
      "id": "0473321f-ae03-4c16-bdf2-1965d35d89db",
      "name": "HTTP Request"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "1d262426-2501-4e1e-94b5-02151323af5b",
              "name": "company",
              "value": "={{ $json.company.replace('.jobs', '').replace('.com', '').replace('.io', '').replace('.co.*', '') }}",
              "type": "string"
            },
            {
              "id": "b264a54e-af74-4090-81d0-02ef5a2b0ea6",
              "name": "position",
              "value": "={{ $json.position }}",
              "type": "string"
            },
            {
              "id": "6d09bf27-bf5d-4018-9eaf-df8d538f3825",
              "name": "source",
              "value": "={{ $json.source }}",
              "type": "string"
            },
            {
              "id": "d9adf7e3-d19b-4eb1-8919-6603ce55248e",
              "name": "applyUrl",
              "value": "={{ $json.applyUrl }}",
              "type": "string"
            },
            {
              "id": "875d3c02-c05d-4c23-880f-df12834599dd",
              "name": "jobId",
              "value": "={{ $json.jobId }}",
              "type": "string"
            },
            {
              "id": "a3caead8-6b45-48e4-953f-d26ac16a6f49",
              "name": "url",
              "value": "={{ $json.url }}",
              "type": "string"
            },
            {
              "id": "a9faf965-9c10-4f6c-9393-31a5f4e88ed3",
              "name": "metadata",
              "value": "={{\n{\n  \"url_domain\": \"$json.metadata.url_domain\",\n  \"title\": \"$json.metadata.title\"\n}\n}}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        224,
        0
      ],
      "id": "432e7d42-c446-47a7-bb28-2bdad751dc68",
      "name": "Set data"
    },
    {
      "parameters": {
        "jsCode": "// --- Helper Function: Normalizes Company Name ---\nfunction normalizeCompanyName(name) {\n  if (!name) return null; // Handle null or empty names\n  let normalized = name.toLowerCase().trim();\n  normalized = normalized.replace(/\\s+(inc|llc|ltd|corp|corporation|group|company|co)\\.?$/g, '');\n  normalized = normalized.replace(/^(the|a|an)\\s+/g, '');\n  normalized = normalized.replace(/\\s+(the|a|an)$/g, '');\n  normalized = normalized.replace(/[^a-z0-9\\s]/g, ''); // Keep only alphanumeric and spaces\n  normalized = normalized.replace(/\\s+/g, ' '); // Collapse multiple spaces\n  return normalized.trim();\n}\n\n// --- Helper Function: Extracts Company from URL (Simplified) ---\nfunction extractCompanyFromUrl(url) {\n  if (!url) return null;\n  try {\n    const urlObj = new URL(url);\n    let hostname = urlObj.hostname;\n    hostname = hostname.replace(/^www\\./, ''); // Remove www.\n\n    // Try to get the main domain part\n    const parts = hostname.split('.');\n    if (parts.length > 1) {\n      // If it's something like example.com or jobs.example.com, take 'example'\n      const companyPart = (parts[parts.length - 2] || parts[0]).trim();\n      if (companyPart && companyPart !== 'co' && companyPart.length > 1) { // Basic sanity\n        return companyPart.charAt(0).toUpperCase() + companyPart.slice(1);\n      }\n    }\n  } catch (error) {\n    // console.error('Error parsing URL for company:', error);\n  }\n  return null;\n}\n\n// --- Helper Function: Extracts Company from Metadata (Simplified) ---\nfunction extractCompanyFromMetadata(metadata) {\n  if (!metadata) return null;\n\n  // Try to find \"Company Name - Job Title\" or \"Job Title | Company Name\"\n  let match = metadata.match(/^(.*?)\\s*[-\u2013\u2014|]\\s*(.*)$/);\n  if (match) {\n    // Check if the first part looks like a company or the second part does\n    let potentialCompany = match[1].trim();\n    let potentialCompany2 = match[2].trim();\n\n    // Prioritize if it doesn't look like a job title\n    const jobTitleKeywords = ['engineer', 'developer', 'manager', 'analyst', 'senior', 'junior', 'staff', 'principal', 'lead'];\n    const looksLikeJobTitle = (text) => jobTitleKeywords.some(keyword => text.toLowerCase().includes(keyword));\n\n    if (!looksLikeJobTitle(potentialCompany) && potentialCompany.length > 2) {\n      return potentialCompany;\n    }\n    if (!looksLikeJobTitle(potentialCompany2) && potentialCompany2.length > 2) {\n      return potentialCompany2;\n    }\n  }\n\n  // Simple fallback: take the first word if it looks capitalized and is long enough\n  match = metadata.match(/^([A-Z][a-zA-Z\\s]+)/);\n  if (match && match[1].trim().length > 2) {\n      return match[1].trim();\n  }\n\n  return null;\n}\n\n\n// --- Main Processing Logic ---\n\n// Safely access webhook data\nconst webhookData = $input.item.json.body || $input.item.json;\n\n// Validate required fields (can throw error if essential data is missing)\nif (!webhookData.url && !webhookData.applyUrl) {\n  throw new Error('Missing required field: url or applyUrl');\n}\n\nconst currentTimestamp = new Date().toISOString();\n\n// Attempt to extract company name in a prioritized order\nlet companyGuess = webhookData.company || null; // 1. From extension's direct scrape\n\nif (!companyGuess) { // 2. From URL\n  companyGuess = extractCompanyFromUrl(webhookData.applyUrl || webhookData.url);\n}\n\nif (!companyGuess) { // 3. From metadata (page title)\n  companyGuess = extractCompanyFromMetadata(webhookData.metadata);\n}\n\n// Normalize the best guess\nconst finalCompanyName = normalizeCompanyName(companyGuess);\n\n\n// Create output data for the job_applications table\nconst outputData = {\n  company_name: finalCompanyName, // Use the normalized company name or null\n  role_title: webhookData.position || null,\n  role_id: webhookData.jobId || null, // Your role_id for job_applications table\n  url: webhookData.applyUrl || webhookData.url, // Ensure URL is never null\n  metadata: webhookData.metadata || null, // Keep the raw metadata\n  source: webhookData.source || 'extension', // Default source\n\n  // FSM Initial State\n  current_state: 'APPLIED',\n  applied_date: currentTimestamp,\n  last_state_change: currentTimestamp,\n\n  // Default nulls for other fields\n  upcoming_date: null,\n  date_context: null\n};\n\n// Return array of items (n8n expects an array)\nreturn [outputData];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        448,
        0
      ],
      "id": "33b2df14-2dbe-4bf7-a080-7486bc041f7d",
      "name": "transition company name to lowercase"
    },
    {
      "parameters": {
        "schema": {
          "__rl": true,
          "mode": "list",
          "value": "public"
        },
        "table": {
          "__rl": true,
          "value": "job_applications",
          "mode": "list",
          "cachedResultName": "job_applications"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "role_title": "={{ $json.role_title }}",
            "current_state": "={{ $json.current_state }}",
            "applied_date": "={{ $json.applied_date }}",
            "last_state_change": "={{ $json.last_state_change }}",
            "source": "={{ $json.source }}",
            "role_id": "={{ $json.role_id }}",
            "company_name": "={{ $json.company_name }}",
            "url": "={{ $json.url }}",
            "metadata": "={{ $json.metadata }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "application_id",
              "displayName": "application_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "role_title",
              "displayName": "role_title",
              "required": true,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "current_state",
              "displayName": "current_state",
              "required": true,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "applied_date",
              "displayName": "applied_date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "dateTime",
              "canBeUsedToMatch": true
            },
            {
              "id": "last_state_change",
              "displayName": "last_state_change",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "dateTime",
              "canBeUsedToMatch": true
            },
            {
              "id": "source",
              "displayName": "source",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "dateTime",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "role_id",
              "displayName": "role_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "upcoming_date",
              "displayName": "upcoming_date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "date_context",
              "displayName": "date_context",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "company_name",
              "displayName": "company_name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "url",
              "displayName": "url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "metadata",
              "displayName": "metadata",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "object",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        656,
        0
      ],
      "id": "35d34168-3dd4-4d8b-b169-b1234b86ea60",
      "name": "Insert into job application table",
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Extension Listener": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Set data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set data": {
      "main": [
        [
          {
            "node": "transition company name to lowercase",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "transition company name to lowercase": {
      "main": [
        [
          {
            "node": "Insert into job application table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false,
    "timeSavedMode": "fixed",
    "callerPolicy": "workflowsFromSameOwner",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "saveExecutionProgress": true,
    "saveManualExecutions": false
  },
  "versionId": "ff1a24e5-797d-4f3f-8b05-fddd362bb70c",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "hIuXtkcbmdht9e4B",
  "tags": []
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

job.application_logger. Uses httpRequest, postgres. Webhook trigger; 5 nodes.

Source: https://github.com/daemon-Ad/Job-Aggregation-and-Email-Intelligence-System/blob/main/workflows/job.application_logger.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

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

Data & Sheets

CMM. Uses httpRequest, postgres, redis. Webhook trigger; 90 nodes.

HTTP Request, Postgres, Redis
Data & Sheets

Scraping. Uses httpRequest, postgres, @apify/n8n-nodes-apify, respondToWebhook. Webhook trigger; 61 nodes.

HTTP Request, Postgres, @Apify/N8N Nodes Apify
Data & Sheets

Workflow B — AI Listing Engine. Uses httpRequest, postgres, errorTrigger. Webhook trigger; 47 nodes.

HTTP Request, Postgres, Error Trigger
Data & Sheets

LogSentinel Workflow. Uses postgres, emailSend, httpRequest. Webhook trigger; 44 nodes.

Postgres, Email Send, HTTP Request
Data & Sheets

Post-Prayer. Uses postgres, httpRequest. Webhook trigger; 44 nodes.

Postgres, HTTP Request