DEV Community

Alex Kane
Alex Kane

Posted on

n8n for Insurance Companies: 5 Automations That Speed Up Claims and Policy Operations (Free Workflow JSON)

If you work in insurance operations, you know the pain: claims pile up waiting for manual routing, policy renewals fall through the cracks, underwriting checklists live in spreadsheets nobody updates, and compliance deadlines sneak up like a rogue wave.

n8n fixes all of this — without sending sensitive policyholder data to a third-party cloud.

Why n8n for insurance?

Insurance companies handle some of the most sensitive data in existence: health records, financial disclosures, accident reports, Social Security numbers. Cloud automation tools like Zapier and Make.com route all of that through their servers. n8n is self-hosted — your data never leaves your network. That's not a nice-to-have for insurance; it's a compliance requirement.

Here are 5 insurance automations you can deploy today, with full importable workflow JSON.


1. Claims Intake & Auto-Triage

The problem: New claims arrive by email, web form, or phone — then sit in a queue until someone manually routes them to the right team.

The automation: Capture every new claim via webhook, classify by type and urgency, route to the right Slack channel, acknowledge the claimant by email, and log to your claims spreadsheet.

{
  "name": "Claims Intake & Auto-Triage",
  "nodes": [
    {"name": "Webhook", "type": "n8n-nodes-base.webhook", "parameters": {"path": "claims-intake", "httpMethod": "POST", "responseMode": "responseNode"}},
    {"name": "Extract & Score Claim", "type": "n8n-nodes-base.code", "parameters": {"jsCode": "const b = $json.body || $json;\nconst claimType = (b.claim_type || '').toLowerCase();\nconst amount = parseFloat(b.estimated_amount || 0);\nconst urgency = amount > 50000 ? 'HIGH' : amount > 10000 ? 'MEDIUM' : 'LOW';\nconst claimId = 'CLM-' + Date.now().toString(36).toUpperCase();\nreturn [{ json: { claimId, claimType, amount, urgency, claimantEmail: b.claimant_email, claimantName: b.claimant_name, receivedAt: new Date().toISOString() } }];"}},
    {"name": "Switch by Type", "type": "n8n-nodes-base.switch", "parameters": {"dataType": "string", "value1": "={{ $json.claimType }}", "rules": {"rules": [{"value2": "auto", "output": 0}, {"value2": "home", "output": 1}, {"value2": "health", "output": 2}, {"value2": "life", "output": 3}]}, "fallbackOutput": 4}},
    {"name": "Slack Auto Claims", "type": "n8n-nodes-base.slack", "parameters": {"channel": "#claims-auto", "text": "={{ 'New AUTO claim ' + $json.claimId + ' | ' + $json.urgency + ' | $' + $json.amount + ' | ' + $json.claimantName }}"}},
    {"name": "Slack Home Claims", "type": "n8n-nodes-base.slack", "parameters": {"channel": "#claims-home", "text": "={{ 'New HOME claim ' + $json.claimId + ' | ' + $json.urgency + ' | $' + $json.amount + ' | ' + $json.claimantName }}"}},
    {"name": "Gmail Claimant ACK", "type": "n8n-nodes-base.gmail", "parameters": {"toList": "={{ $json.claimantEmail }}", "subject": "={{ 'Claim ' + $json.claimId + ' received — we will be in touch within 24h' }}", "message": "={{ 'Hi ' + $json.claimantName + ', your claim ' + $json.claimId + ' is under review. We will contact you within 24 business hours.' }}"}},
    {"name": "Log to Sheets", "type": "n8n-nodes-base.googleSheets", "parameters": {"operation": "append", "spreadsheetId": "YOUR_SPREADSHEET_ID", "range": "Claims!A:H"}}
  ]
}
Enter fullscreen mode Exit fullscreen mode

2. Policy Renewal Alert Pipeline

The problem: Policies lapse because renewal reminders go out too late — or not at all. Agents scramble, customers cancel.

The automation: Run daily, check every policy's expiration date, send tiered reminders (90 / 60 / 30 / 14 / 7 days out) to both the policyholder and their agent.

{
  "name": "Policy Renewal Alert Pipeline",
  "nodes": [
    {"name": "Daily 9AM", "type": "n8n-nodes-base.scheduleTrigger", "parameters": {"rule": {"interval": [{"field": "cronExpression", "expression": "0 9 * * *"}]}}},
    {"name": "Get Policy Database", "type": "n8n-nodes-base.googleSheets", "parameters": {"operation": "getAll", "spreadsheetId": "YOUR_SPREADSHEET_ID", "range": "Policies!A:F"}},
    {"name": "Calculate Days to Renewal", "type": "n8n-nodes-base.code", "parameters": {"jsCode": "const today = new Date();\nconst alerts = [];\nfor (const item of $input.all()) {\n  const d = item.json;\n  const expDate = new Date(d.expiration_date);\n  const daysLeft = Math.round((expDate - today) / 86400000);\n  if ([90, 60, 30, 14, 7].includes(daysLeft)) {\n    const urgency = daysLeft <= 14 ? 'URGENT' : daysLeft <= 30 ? 'WARNING' : 'NOTICE';\n    alerts.push({ json: { ...d, daysLeft, urgency } });\n  }\n}\nreturn alerts;"}},
    {"name": "Gmail Policyholder", "type": "n8n-nodes-base.gmail", "parameters": {"toList": "={{ $json.policyholder_email }}", "subject": "={{ '[' + $json.urgency + '] Your ' + $json.policy_type + ' policy renews in ' + $json.daysLeft + ' days' }}", "message": "={{ 'Dear ' + $json.policyholder_name + ', your policy expires in ' + $json.daysLeft + ' days. Contact your agent ' + $json.agent_name + ' to renew.' }}"}},
    {"name": "Slack Agent Alert", "type": "n8n-nodes-base.slack", "parameters": {"channel": "#renewals", "text": "={{ $json.urgency + ': Policy ' + $json.policy_number + ' for ' + $json.policyholder_name + ' expires in ' + $json.daysLeft + ' days. Agent: ' + $json.agent_name }}"}}
  ]
}
Enter fullscreen mode Exit fullscreen mode

3. Underwriting Checklist Automation

The problem: Every new application requires a checklist — credit check, risk assessment, document verification — tracked manually in email chains.

The automation: Trigger on a new application in Sheets, calculate a preliminary risk score, email the underwriter a checklist, and follow up automatically if incomplete after 3 days.

{
  "name": "Underwriting Checklist Automation",
  "nodes": [
    {"name": "New Application Trigger", "type": "n8n-nodes-base.googleSheetsTrigger", "parameters": {"spreadsheetId": "YOUR_SPREADSHEET_ID", "range": "Applications!A:G", "event": "rowAdded"}},
    {"name": "Calculate Risk Score", "type": "n8n-nodes-base.code", "parameters": {"jsCode": "const d = $json;\nlet score = 0;\nif (d.credit_score > 750) score += 20;\nelse if (d.credit_score > 650) score += 10;\nelse score -= 10;\nif (d.prior_claims === '0') score += 15;\nelse if (d.prior_claims === '1') score += 5;\nelse score -= 15;\nconst riskTier = score >= 30 ? 'LOW' : score >= 10 ? 'MEDIUM' : 'HIGH';\nconst applicationId = 'APP-' + Date.now().toString(36).toUpperCase();\nreturn [{ json: { ...d, riskScore: score, riskTier, applicationId } }];"}},
    {"name": "Gmail Underwriter", "type": "n8n-nodes-base.gmail", "parameters": {"toList": "={{ $json.underwriter_email }}", "subject": "={{ 'New Application ' + $json.applicationId + ' | Risk: ' + $json.riskTier + ' | Action required' }}", "message": "={{ 'Underwriting checklist for ' + $json.applicationId + ' | Applicant: ' + $json.applicant_name + ' | Type: ' + $json.policy_type + ' | Risk tier: ' + $json.riskTier + ' (score: ' + $json.riskScore + ')\\n\\nRequired: credit pull, loss history, property inspection, document verification, final risk confirmation. Target: 3 business days.' }}"}},
    {"name": "Slack Underwriting", "type": "n8n-nodes-base.slack", "parameters": {"channel": "#underwriting", "text": "={{ 'New app ' + $json.applicationId + ' | ' + $json.applicant_name + ' | ' + $json.policy_type + ' | Risk: ' + $json.riskTier + ' | Assigned: ' + $json.underwriter_name }}"}},
    {"name": "Wait 3 Days", "type": "n8n-nodes-base.wait", "parameters": {"amount": 3, "unit": "days"}},
    {"name": "IF Not Complete", "type": "n8n-nodes-base.if", "parameters": {"conditions": {"string": [{"value1": "={{ $json.underwriting_status }}", "operation": "notEqual", "value2": "Complete"}]}}},
    {"name": "Gmail Follow-Up", "type": "n8n-nodes-base.gmail", "parameters": {"toList": "={{ $json.underwriter_email }}", "subject": "={{ 'FOLLOW-UP: Application ' + $json.applicationId + ' checklist incomplete' }}", "message": "={{ 'Application ' + $json.applicationId + ' for ' + $json.applicant_name + ' has been open 3 days with no checklist completion. Please update the Applications sheet or contact your manager.' }}"}}
  ]
}
Enter fullscreen mode Exit fullscreen mode

4. Compliance & Regulatory Deadline Tracker

The problem: Missing a compliance filing means fines, license suspension, or worse. Deadlines are tracked in a spreadsheet nobody checks until it's too late.

The automation: Run every morning, scan the compliance calendar, send tiered alerts (30 / 14 / 7 / 1 day) to the compliance officer with everything needed to act.

{
  "name": "Compliance & Regulatory Deadline Tracker",
  "nodes": [
    {"name": "Daily 8AM", "type": "n8n-nodes-base.scheduleTrigger", "parameters": {"rule": {"interval": [{"field": "cronExpression", "expression": "0 8 * * *"}]}}},
    {"name": "Get Compliance Calendar", "type": "n8n-nodes-base.googleSheets", "parameters": {"operation": "getAll", "spreadsheetId": "YOUR_SPREADSHEET_ID", "range": "Compliance!A:E"}},
    {"name": "Filter Upcoming Deadlines", "type": "n8n-nodes-base.code", "parameters": {"jsCode": "const today = new Date();\nconst alerts = [];\nfor (const item of $input.all()) {\n  const d = item.json;\n  if (d.status === 'Complete') continue;\n  const deadline = new Date(d.deadline_date);\n  const daysLeft = Math.round((deadline - today) / 86400000);\n  if ([30, 14, 7, 1].includes(daysLeft) || daysLeft < 0) {\n    const severity = daysLeft < 0 ? 'OVERDUE' : daysLeft <= 1 ? 'CRITICAL' : daysLeft <= 7 ? 'URGENT' : 'WARNING';\n    alerts.push({ json: { ...d, daysLeft, severity } });\n  }\n}\nreturn alerts;"}},
    {"name": "Slack Compliance", "type": "n8n-nodes-base.slack", "parameters": {"channel": "#compliance", "text": "={{ $json.severity + ': ' + $json.regulation_name + ' due in ' + $json.daysLeft + ' days (' + $json.deadline_date + '). Owner: ' + $json.owner }}"}},
    {"name": "Gmail Compliance Officer", "type": "n8n-nodes-base.gmail", "parameters": {"toList": "={{ $json.owner_email }}", "subject": "={{ '[' + $json.severity + '] Compliance deadline in ' + $json.daysLeft + ' days: ' + $json.regulation_name }}", "message": "={{ 'Regulation: ' + $json.regulation_name + '\\nDeadline: ' + $json.deadline_date + '\\nDays remaining: ' + $json.daysLeft + '\\nSeverity: ' + $json.severity + '\\nAction required: ' + $json.action_required + '\\n\\nMark complete in the Compliance sheet when done.' }}"}}
  ]
}
Enter fullscreen mode Exit fullscreen mode

5. Agent Performance & Commission Report

The problem: Regional managers get commission data once a month from finance. By then, the month is over and there's nothing left to coach.

The automation: Pull CRM data every Friday, calculate per-agent KPIs (policies written, premiums, renewal rate, commission), email a formatted HTML report to the manager, and Slack a shoutout to the top performer.

{
  "name": "Agent Performance & Commission Report",
  "nodes": [
    {"name": "Friday 4PM", "type": "n8n-nodes-base.scheduleTrigger", "parameters": {"rule": {"interval": [{"field": "cronExpression", "expression": "0 16 * * 5"}]}}},
    {"name": "Get CRM Data", "type": "n8n-nodes-base.googleSheets", "parameters": {"operation": "getAll", "spreadsheetId": "YOUR_SPREADSHEET_ID", "range": "Policies!A:H"}},
    {"name": "Calculate Agent KPIs", "type": "n8n-nodes-base.code", "parameters": {"jsCode": "const agents = {};\nconst weekStart = new Date(Date.now() - 7 * 86400000);\nfor (const item of $input.all()) {\n  const d = item.json;\n  if (new Date(d.written_date) < weekStart) continue;\n  if (!agents[d.agent_name]) agents[d.agent_name] = { agentName: d.agent_name, agentEmail: d.agent_email, policies: 0, premiums: 0, renewals: 0, commission: 0 };\n  agents[d.agent_name].policies++;\n  agents[d.agent_name].premiums += parseFloat(d.annual_premium || 0);\n  if (d.is_renewal === 'TRUE') agents[d.agent_name].renewals++;\n  agents[d.agent_name].commission += parseFloat(d.commission || 0);\n}\nconst rows = Object.values(agents).map(a => ({ json: { ...a, renewalRate: a.policies > 0 ? Math.round(100 * a.renewals / a.policies) : 0 } }));\nrows.sort((a, b) => b.json.premiums - a.json.premiums);\nreturn rows;"}},
    {"name": "Build HTML Report", "type": "n8n-nodes-base.code", "parameters": {"jsCode": "const rows = $input.all();\nlet tableRows = rows.map(r => `<tr><td>${r.json.agentName}</td><td>${r.json.policies}</td><td>$${r.json.premiums.toFixed(0)}</td><td>${r.json.renewalRate}%</td><td>$${r.json.commission.toFixed(0)}</td></tr>`).join('');\nconst html = `<h2>Weekly Agent Performance Report</h2><table border='1' cellpadding='6'><tr><th>Agent</th><th>Policies</th><th>Premiums</th><th>Renewal Rate</th><th>Commission</th></tr>${tableRows}</table>`;\nconst top = rows[0]?.json;\nreturn [{ json: { html, topAgent: top?.agentName, topPremiums: top?.premiums?.toFixed(0) } }];"}},
    {"name": "Gmail Regional Manager", "type": "n8n-nodes-base.gmail", "parameters": {"toList": "manager@yourcompany.com", "subject": "Weekly Agent Performance Report", "message": "={{ $json.html }}", "options": {"bodyContentType": "html"}}},
    {"name": "Slack Top Performer", "type": "n8n-nodes-base.slack", "parameters": {"channel": "#insurance-team", "text": "={{ 'Top agent this week: ' + $json.topAgent + ' with $' + $json.topPremiums + ' in premiums. Great work!' }}"}}
  ]
}
Enter fullscreen mode Exit fullscreen mode

Why insurance teams choose n8n over Zapier or Make.com

Factor n8n Zapier Make.com
Data hosting Self-hosted (your network) Zapier cloud Make.com cloud
Policyholder PII Never leaves your infra Routed through Zapier Routed through Make
SOC 2 / state regs Your responsibility, your control Vendor dependency Vendor dependency
Cost at scale ~$0 (self-hosted) $299–$799/month $99–$500/month
Audit trail Git-versionable JSON Proprietary Proprietary

For insurance, self-hosting isn't just a cost optimization — it's a compliance posture. When regulators ask how you handle policyholder data, "we self-host and it never leaves our network" is a much better answer than "we route it through a third-party SaaS."


Get all 5 workflows

These workflows (and 10 more for insurance, financial services, and compliance teams) are ready to import at FlowKit — stripeai.gumroad.com.

Drop the JSON into n8n's Settings → Import Workflow, swap in your Spreadsheet IDs and Slack channels, and you're live in under 10 minutes.


Questions about adapting these for your specific insurance stack? Drop a comment — happy to help.

Top comments (0)