AutomationFlowsData & Sheets › User-Loan Matching with Postgres

User-Loan Matching with Postgres

Original n8n title: Workflow B: User-loan Matching

Workflow B: User-Loan Matching. Uses postgres. Webhook trigger; 5 nodes.

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

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": "Workflow B: User-Loan Matching",
  "nodes": [
    {
      "parameters": {
        "path": "user-matching",
        "responseMode": "responseNode",
        "options": {}
      },
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        250,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "-- Stage 1: SQL Pre-filter (Fast Path)\nSELECT \n  u.user_id,\n  u.email,\n  u.monthly_income,\n  u.credit_score,\n  u.age,\n  u.employment_status,\n  p.product_id,\n  p.product_name,\n  p.lender_name,\n  p.interest_rate,\n  p.min_income,\n  p.min_credit_score\nFROM users u\nCROSS JOIN loan_products p\nWHERE u.monthly_income >= p.min_income\n  AND u.credit_score >= p.min_credit_score\n  AND u.age BETWEEN p.min_age AND p.max_age\n  AND (p.employment_required = FALSE OR u.employment_status IN ('employed', 'self-employed'))\nORDER BY u.user_id, p.interest_rate ASC"
      },
      "name": "Stage 1: SQL Pre-filter",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 1,
      "position": [
        450,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "functionCode": "// Stage 2: Rule-based Scoring\nconst matches = items.map(item => {\n  const data = item.json;\n  \n  // Calculate match score (0-100)\n  const incomeScore = Math.min(30, (data.monthly_income - data.min_income) / data.min_income * 30);\n  const creditScore = Math.min(70, (data.credit_score - data.min_credit_score) / (850 - data.min_credit_score) * 70);\n  const matchScore = Math.round(incomeScore + creditScore);\n  \n  // Generate match reason\n  const reason = `User exceeds minimum income by ${Math.round((data.monthly_income / data.min_income - 1) * 100)}% and has credit score ${data.credit_score} (min: ${data.min_credit_score})`;\n  \n  return {\n    json: {\n      user_id: data.user_id,\n      product_id: data.product_id,\n      match_score: matchScore,\n      match_reason: reason,\n      ai_enhanced: false\n    }\n  };\n});\n\nreturn matches.filter(m => m.json.match_score >= 60); // Filter out low matches"
      },
      "name": "Stage 2: Rule-based Scoring",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        650,
        300
      ]
    },
    {
      "parameters": {
        "operation": "insert",
        "schema": "public",
        "table": "matches",
        "columns": "user_id,product_id,match_score,match_reason,ai_enhanced"
      },
      "name": "Stage 3: Save Matches to DB",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 1,
      "position": [
        850,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({success: true, matches_created: $items().length}) }}"
      },
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1050,
        300
      ]
    }
  ],
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Stage 1: SQL Pre-filter",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 1: SQL Pre-filter": {
      "main": [
        [
          {
            "node": "Stage 2: Rule-based Scoring",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 2: Rule-based Scoring": {
      "main": [
        [
          {
            "node": "Stage 3: Save Matches to DB",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 3: Save Matches to DB": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {},
  "id": "workflow-b"
}

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

Workflow B: User-Loan Matching. Uses postgres. Webhook trigger; 5 nodes.

Source: https://github.com/kaustubhduse/clickpe-backend/blob/a6f49ab0e0c4c8ef9d329e25c78912fab03f21ea/n8n-workflows/workflow-b-matching.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

Projects. Uses postgres. Webhook trigger; 58 nodes.

Postgres
Data & Sheets

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

HTTP Request, Postgres, Error Trigger
Data & Sheets

How it works

Postgres, Email Send