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"}}
]
}
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 }}"}}
]
}
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.' }}"}}
]
}
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.' }}"}}
]
}
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!' }}"}}
]
}
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)