Demo- Build an AI Loan Pre-Screening Agent with n8n, GPT-4o-mini & Neon Postgres

Automation June 2026 SecureBank Technology Team 12 min read
🏦 Case Study · AI + Fintech Automation

AI-Powered Loan Pre-Screening Agent — built on n8n

A complete technical walkthrough: how SecureBank India automated loan intake decisions in under 10 seconds using n8n, OpenAI GPT-4o-mini, Neon Postgres, and Resend.

<10s per decision
11 nodes end-to-end
3 outcomes Approve · Review · Reject
Zero manual intake bias eliminated
01 — Executive summary

Automating loan intake from form to decision

SecureBank India implemented an intelligent loan pre-screening agent that automates the initial evaluation of loan applications. The system integrates a web-based application form, a simulated credit bureau lookup, an AI decision engine powered by OpenAI GPT-4o-mini, automated email notifications via Resend, and a persistent audit database on Neon Postgres — all orchestrated through the n8n workflow automation platform.

Instant decisions

Pre-screening decisions delivered in under 10 seconds per application, 24/7, with no human in the loop.

🤖

Consistent AI evaluation

Rule-based GPT-4o-mini scoring eliminates inconsistency and human bias at the intake stage.

📧

Automated notifications

Applicants and loan officers receive tailored emails the moment a decision is made.

🗄

Full audit trail

Every decision, risk score, and AI flag logged to Neon Postgres for regulatory compliance.

🎯

Three-tier routing

Approve, Manual Review, or Reject — each with its own email flow and officer alert logic.

📝

Zero-code form

Applicant-facing form hosted inside n8n — no separate frontend or external tool needed.


02 — Technology stack

Four tools. One pipeline.

All components are cloud-native and free-tier friendly — ideal for a working demo that can go to production without a rewrite.

n8n — Workflow engine
OpenAI GPT-4o-mini
Neon Postgres (serverless)
Resend Email API
CIBIL (simulated → real in prod)
ComponentTechnologyNotes
Workflow Enginen8nSelf-hosted or n8n Cloud
Application Formn8n Form TriggerBuilt-in hosted form, no frontend needed
AI Decision EngineOpenAI GPT-4o-miniVia n8n AI Agent node
Email ServiceResend APITransactional email
DatabaseNeon PostgresServerless, cloud, free tier available
Credit BureauSimulated CIBILCode node — replace with real API in production
Deploymentn8n Cloud or DockerSelf-hosted option available

03 — System architecture

11 nodes, one linear flow, three possible outcomes

The workflow is linear until Node 6 (the Switch). Data passes from one node to the next, accumulating context — form fields, credit score, DTI ratio, and finally the AI decision — before branching into three email paths.

1
Loan Application Form
Form Trigger
Hosted form inside n8n capturing 9 applicant fields: name, email, phone, DOB, employment status, income, loan amount, purpose, and PAN number.
2
Validate & Prepare Data
Code · JavaScript
Validates all required fields, calculates age from DOB (rejects outside 21–65), and computes DTI ratio: (loan × 2%) ÷ income × 100.
3
Mock Credit Bureau
Code · JavaScript
Simulates a CIBIL score (base 650, adjusted by income, DTI, employment). Replace with a real CIBIL/Experian HTTP Request in production.
4
Loan Screening AI
AI Agent · GPT-4o-mini
The decision core. Receives all applicant data and returns a structured JSON with decision, risk score (0–100), summary, flags, recommended amount, and interest band. Temperature set to 0.1 for consistency.
5
Parse AI Response
Code · JavaScript
Extracts structured fields from AI text output. Strips accidental markdown fences. Throws a clear error if JSON is malformed.
6
Route Decision
Switch
Reads decision field and routes: approve → applicant email, manual_review → officer alert, reject → rejection email.
7–9
Email Notifications (3 branches)
HTTP Request · Resend API
Approval email with loan details and interest band → applicant. Officer alert with full risk data and flags → loan officer. Rejection email with 90-day reapplication guidance → applicant.
10
Merge All Branches
Merge · Append
Reunites all three decision branches into a single flow so the audit logging step runs regardless of outcome.
11
Save to Neon Postgres
Postgres · Execute Query
Inserts the complete record — AI decision, risk score, credit score, DTI, flags, timestamps — into the loan_applications audit table.

Data flow

Each node passes its complete output to the next node. By the time data reaches the Switch node, the JSON object contains all original form fields plus enrichment data (credit score, DTI ratio) and AI output (decision, risk score, flags, recommended amount, interest band).


04 — Decision logic

Three outcomes, driven by three signals

The AI evaluates every application against credit score, debt-to-income ratio, and employment stability. Each combination maps to one of three outcomes.

✓ Approve

All criteria met

  • Credit score 720 or above
  • DTI ratio below 35%
  • Salaried or business owner
  • Age between 21 and 65

Approval email sent to applicant instantly

⚠ Manual review

Borderline signals

  • Credit score 600–719
  • DTI ratio 35%–50%
  • Self-employed applicant
  • Any one elevated flag

Loan officer alerted with full risk breakdown

✗ Reject

Clear disqualifiers

  • Credit score below 600
  • DTI ratio above 50%
  • Currently unemployed
  • Age outside 21–65 range

Polite rejection email sent with reapplication guidance

🤖
How the AI prompt works
The system prompt instructs GPT-4o-mini to respond only with a valid JSON object — no prose, no markdown. Temperature is 0.1 for highly consistent decisions. Recommended loan amount is capped at 60× monthly income. Interest band is derived from the returned risk score.

05 — Prerequisites & credentials

Setting up before you build

Neon Postgres — database setup

  1. Go to console.neon.tech and create a new project
  2. Project name: loan-prescreening-demo — Region: AWS ap-south-1 (Mumbai)
  3. Copy the connection string — needed for the n8n credential
  4. Open the SQL Editor and run the table creation script below

Database table SQL

CREATE TABLE loan_applications (
  id              SERIAL PRIMARY KEY,
  applicant_name  TEXT NOT NULL,
  email           TEXT NOT NULL,
  phone           TEXT,
  age             INT,
  employment      TEXT,
  monthly_income  NUMERIC(12,2),
  loan_amount     NUMERIC(12,2),
  loan_purpose    TEXT,
  credit_score    INT,
  dti_ratio       NUMERIC(5,2),
  decision        TEXT,
  risk_score      INT,
  ai_summary      TEXT,
  flags           TEXT,
  submitted_at    TIMESTAMPTZ DEFAULT NOW(),
  processed_at    TIMESTAMPTZ
);

n8n Neon credential settings

Credential nameNeon-LoanDB
Hostep-xxxx.ap-southeast-1.aws.neon.tech
Databaseneondb
Userneondb_owner
Password<your neon password>
Port5432
SSLEnabled (required by Neon)

Resend Email — credential setup

  1. Go to resend.com and create an account
  2. Navigate to API Keys → Create API Key — Name: n8n-loan-agent, Permission: Full Access
  3. Copy the key (starts with re_...)
  4. For demo: use onboarding@resend.dev as sender — works without domain verification
Credential typeHeader Auth
Credential nameResend-API
Header nameAuthorization
Header valueBearer re_YOUR_API_KEY_HERE

OpenAI — API key setup

  1. Go to platform.openai.com and create an API key
  2. In n8n: Settings → Credentials → Add Credential → OpenAI API
  3. Credential name: OpenAI-LoanAgent — paste key, Save, then Test credential

06 — Node-by-node configuration

What each node actually does

Click any node below to expand configuration details and key implementation notes.

1 Loan Application Form n8n Form Trigger · URL path: /loan-apply
Node typen8n Form Trigger
Node nameLoan Application Form
URL path/loan-apply
Form titleApply for a Loan — SecureBank
Button labelSubmit Application

Form fields: Full Name (Text, Required), Email Address (Email, Required), Phone Number (Text, Optional), Date of Birth (Date, Required), Employment Status (Dropdown: Salaried / Self-Employed / Business Owner / Unemployed, Required), Monthly Income (Number, Required), Loan Amount (Number, Required), Loan Purpose (Dropdown: Home / Car / Personal / Education / Business Loan, Required), PAN Number (Text, Required).

Do not add currency symbols in field labels
Do NOT add ₹ or Rs in field labels. These cause field name mismatches in subsequent Code nodes. Use plain labels: “Monthly Income”, “Loan Amount”.
2 Validate & Prepare Data Code node · JavaScript

Trims all field keys to handle leading/trailing spaces, validates required fields, calculates age from DOB (throws error if outside 21–65), and computes DTI ratio using estimatedEMI = loanAmount × 0.02 then DTI = (EMI ÷ monthlyIncome) × 100.

const d = $input.first().json;
const data = {};
Object.keys(d).forEach(key => { data[key.trim()] = d[key]; });

const name   = data['Full Name']?.toString().trim();
const email  = data['Email Address']?.toString().trim();
const income = parseFloat(data['Monthly Income'] || 0);
const loan   = parseFloat(data['Loan Amount'] || 0);
const dob    = data['Date of Birth'];
const emp    = data['Employment Status'];
const purpose= data['Loan Purpose'];
const pan    = data['PAN Number']?.toString().trim();

const missing = [];
if (!name)   missing.push('Full Name');
if (!email)  missing.push('Email');
if (!income) missing.push('Monthly Income');
if (!loan)   missing.push('Loan Amount');
if (!dob)    missing.push('Date of Birth');
if (!pan)    missing.push('PAN Number');
if (missing.length > 0) throw new Error('Missing: ' + missing.join(', '));

const age = Math.floor((new Date() - new Date(dob)) / (365.25*24*3600*1000));
if (age < 21 || age > 65) throw new Error('Age must be 21-65. Got: ' + age);

const estimatedEMI = loan * 0.02;
const dtiRatio = parseFloat(((estimatedEMI / income) * 100).toFixed(2));

return [{ json: {
  applicant_name: name, email, phone: data['Phone Number'] || '',
  age, employment: emp, monthly_income: income, loan_amount: loan,
  loan_purpose: purpose, pan_number: pan, dti_ratio: dtiRatio,
  submitted_at: data['submittedAt'] || new Date().toISOString()
}}];
3 Mock Credit Bureau Code node · Replace with real CIBIL API in production

Simulates a CIBIL score for demo purposes. Starts at base 650, adjusts for income level (±60), DTI ratio (±80), and employment status (+20 salaried, −150 unemployed). Adds small random variation for realism. In production, replace this node with an HTTP Request to CIBIL/Experian using the applicant’s PAN number.

const d = $input.first().json;
let baseScore = 650;
if (d.monthly_income > 100000)      baseScore += 60;
else if (d.monthly_income > 60000)  baseScore += 30;
else if (d.monthly_income < 30000)  baseScore -= 50;

if (d.dti_ratio < 20)       baseScore += 40;
else if (d.dti_ratio > 50)  baseScore -= 80;
else if (d.dti_ratio > 35)  baseScore -= 30;

if (d.employment === 'Salaried')    baseScore += 20;
if (d.employment === 'Unemployed')  baseScore -= 150;

baseScore += Math.floor(Math.random() * 30) - 10;
const creditScore = Math.min(900, Math.max(300, baseScore));

return [{ json: { ...d, credit_score: creditScore,
  bureau_name: 'CIBIL (Simulated)', bureau_status: 'Active',
  checked_at: new Date().toISOString() }}];
4 Loan Screening AI (GPT-4o-mini) AI Agent node · Temperature: 0.1 · Max tokens: 600
Sub-node typeOpenAI Chat Model
CredentialOpenAI-LoanAgent
Modelgpt-4o-mini
Temperature0.1
Max Tokens600
Configure the prompt source
In the AI Agent node, find “Source for Prompt (User Message)” and change it from “Connected Chat Trigger Node” to “Define below”. The Prompt field then becomes editable.

System message

You are a senior loan pre-screening AI for SecureBank India.
Evaluate the applicant data and respond ONLY with a valid JSON object.
No markdown, no explanation outside the JSON.

JSON schema you must follow exactly:
{
  "decision": "approve" | "manual_review" | "reject",
  "risk_score": integer between 0 and 100 (higher = riskier),
  "summary": "2 sentence plain English explanation",
  "flags": ["concern 1", "concern 2"],
  "recommended_amount": number or null,
  "interest_band": "e.g. 8.5%-10.2%" or null
}

Decision rules:
- approve       : credit_score >= 720 AND DTI < 35% AND stable employment
- manual_review : credit_score 600-719 OR DTI 35-50% OR self-employed
- reject        : credit_score < 600 OR DTI > 50% OR unemployed
- Always reject if age < 21 or age > 65
- recommended_amount must not exceed 60x monthly income

User message / prompt

Applicant: {{ $json.applicant_name }}
Age: {{ $json.age }}
Employment: {{ $json.employment }}
Monthly Income: Rs {{ $json.monthly_income }}
Loan Requested: Rs {{ $json.loan_amount }}
Loan Purpose: {{ $json.loan_purpose }}
Credit Score: {{ $json.credit_score }}
DTI Ratio: {{ $json.dti_ratio }}%
PAN: {{ $json.pan_number }}
5 Parse AI Response Code node · JavaScript

Reads the AI output field (tries output, text, response, message.content for n8n version compatibility), strips markdown fences, parses JSON, and merges structured fields back into the item.

const input = $input.first().json;
const raw = input.output || input.text || input.response
  || input.message?.content || input.choices?.[0]?.message?.content || '';

const cleaned = raw.replace(/```json/gi,'').replace(/```/g,'').trim();
let ai;
try { ai = JSON.parse(cleaned); }
catch(e) { throw new Error('AI did not return valid JSON. Raw: ' + raw); }

return [{ json: { ...input,
  decision: ai.decision, risk_score: ai.risk_score,
  ai_summary: ai.summary,
  flags: Array.isArray(ai.flags) ? ai.flags.join(', ') : '',
  recommended_amount: ai.recommended_amount || null,
  interest_band: ai.interest_band || null
}}];
6 Route Decision (Switch node) Evaluates {{ $json.decision }}
Field to evaluate{{ $json.decision }}
Route 1equals approve → Send Approval Email
Route 2equals manual_review → Alert Loan Officer
Route 3equals reject → Send Rejection Email
7–9 Email Notifications — 3 branches HTTP Request · POST to Resend API

All three nodes POST to https://api.resend.com/emails with Header Auth → Resend-API. Each sends a different HTML email body with n8n expressions injecting applicant data.

Node 7 — ApprovalSent to {{ $json.email }} — includes loan amount, recommended amount, interest band, risk score
Node 8 — Officer alertSent to loan.officer@yourdomain.com — includes full applicant data, credit score, DTI, flags
Node 9 — RejectionSent to {{ $json.email }} — polite decline, 90-day reapplication guidance
10–11 Merge + Save to Neon Postgres Merge (Append mode) then Postgres Execute Query

The Merge node (Append mode) reunites all three branches. The Postgres node then inserts the complete record. The query uses RETURNING id so the application ID is available for downstream use.

INSERT INTO loan_applications (
  applicant_name, email, phone, age, employment,
  monthly_income, loan_amount, loan_purpose,
  credit_score, dti_ratio, decision, risk_score,
  ai_summary, flags, processed_at
) VALUES (
  '{{ $json.applicant_name }}', '{{ $json.email }}',
  '{{ $json.phone }}', {{ $json.age }},
  '{{ $json.employment }}', {{ $json.monthly_income }},
  {{ $json.loan_amount }}, '{{ $json.loan_purpose }}',
  {{ $json.credit_score }}, {{ $json.dti_ratio }},
  '{{ $json.decision }}', {{ $json.risk_score }},
  '{{ $json.ai_summary }}', '{{ $json.flags }}', NOW()
) RETURNING id;

07 — Demo test scenarios

Three test cases, every decision path covered

Submit these in sequence during a client demo. Each email arrives within 10 seconds of form submission.

RM
Rahul Mehta
IT Professional · Salaried
Monthly income₹1,20,000
Loan requested₹15,00,000
DOB1990-05-15
Expected credit score~780
PurposeHome Loan
PANABCPM1234D
✓ Expected: APPROVE

Strong income, stable employment, low DTI. Risk score 15–30 range.

PS
Priya Sharma
Freelance Consultant · Self-Employed
Monthly income₹45,000
Loan requested₹8,00,000
DOB1985-11-22
Expected credit score~640
PurposeBusiness Loan
PANCDEPM5678F
⚠ Expected: MANUAL REVIEW

Self-employed flag + borderline DTI. Loan officer receives detailed alert.

AK
Arun Kumar
Currently Unemployed
Monthly income₹12,000
Loan requested₹6,00,000
DOB2000-03-10
Expected credit score~420
PurposePersonal Loan
PANFGHPK9012G
✗ Expected: REJECT

Unemployed, very high DTI, low credit score. Automatic rejection.

Verify data in Neon after the demo

SELECT id, applicant_name, decision, risk_score,
       credit_score, dti_ratio, flags, processed_at
FROM loan_applications
ORDER BY processed_at DESC
LIMIT 10;

08 — Troubleshooting

Common issues and how to fix them

🔴 “Missing required fields” error in Node 2
A form field label has a leading or trailing space. Open the Form Trigger node, check each field label, and remove any extra whitespace. The Code node trims all keys, but the label in the trigger definition must already be clean.
🔴 “No prompt specified” error in the AI Agent
Change “Source for Prompt” from “Connected Chat Trigger Node” to “Define below” inside the AI Agent node. The Prompt field will then become editable — paste the user message template with n8n expressions.
🟡 AI returned invalid JSON error
The Parse AI Response node automatically strips markdown fences. Check the raw output field in node execution logs. If consistently malformed, tighten the system prompt to reinforce JSON-only output.
🟡 Emails are not being delivered
For demo use, set sender to onboarding@resend.dev — works without domain verification. For production, verify your domain in the Resend dashboard. Confirm the Authorization header starts with Bearer re_.
🔴 Neon Postgres connection refused
Neon requires SSL — the connection is refused without it. In n8n’s Postgres credential settings, ensure SSL is toggled on. Verify the host, database name, and password match your Neon project dashboard exactly.
🟡 Switch node not routing correctly
The Switch does a plain string equality match. Ensure Node 5 runs first and that the decision field contains exactly approve, manual_review, or reject — no uppercase, no extra whitespace. Check Node 5’s output panel.
🟡 Credit score always the same
The mock bureau adds slight randomness via Math.random(). For a fully consistent demo score, remove the Math.random() line from Node 3.

09 — Production upgrade path

Five changes before going live

The demo is intentionally minimal. Here is what to add before putting real applicants through the system.

Step 01

Real credit bureau API

Replace Node 3 with an HTTP Request to CIBIL or Experian. Pass the PAN number as the lookup key and map the response into the same credit_score field.

Step 02

KYC & identity verification

Add a parallel branch after Node 2 calling Onfido or DigiLocker. Merge the KYC result with the credit score before feeding the AI Agent.

Step 03

Document upload

Add file upload fields to the Form Trigger for salary slips and bank statements. Store in S3 or Cloud Storage, then pass URLs to the AI Agent.

Step 04

Applicant status portal

Build a status-check page querying Neon Postgres by application ID. Add the ID to confirmation emails so applicants can self-serve.

Step 05

Security hardening

  • Enable n8n webhook authentication
  • Add rate limiting to the Form Trigger
  • Store all credentials in n8n vault
  • Enable Neon row-level security for multi-branch deployments

Ready to build your own AI screening agent?

Every node, prompt, and SQL query is documented above. Zero to working demo in an afternoon.

#n8n #OpenAI #LoanAutomation #FintechAI #NeonPostgres #WorkflowAutomation #GPT4o
Scroll to Top