Skip to content

Loss Runs

Loss runs are the historical claims record that underwriters rely on to evaluate a risk. The @openinsure/loss-runs package handles the full lifecycle: requesting loss runs from prior carriers, extracting data from PDF and CSV formats, analyzing trends, and flagging risks.


Producer submits new business
Request loss runs from prior carriers
Carrier responds (PDF or CSV attachment)
Extract structured data (AI + parser)
5-year loss analysis + trend detection
Risk flags surfaced in UW queue
Underwriter reviews → Quote / Decline / Refer

Loss run requests are sent via the submission workflow when a submission is received:

Terminal window
POST /v1/submissions/:id/request-loss-runs
Authorization: Bearer <token>
Content-Type: application/json
{
"priorCarrier": {
"name": "Acme Mutual Insurance",
"claimsEmail": "lossruns@acmemutual.com",
"policyNumber": "GL-2022-001234"
},
"yearsRequested": 5,
"effectiveDateRange": {
"from": "2020-01-01",
"to": "2025-01-01"
}
}

This sends a templated email to the prior carrier’s claims department and creates a pending request record. When the carrier replies with an attachment, the shared mailbox cron routes the attachment to the loss run extractor.


The @openinsure/loss-runs package includes both a structured CSV extractor and an AI-powered PDF extractor.

import { extractFromCSV } from '@openinsure/loss-runs';
const lossRun = await extractFromCSV(csvString, {
mapping: {
claimNumber: 'Claim #',
dateOfLoss: 'Loss Date',
status: 'Status',
paidLoss: 'Paid Indemnity',
paidExpense: 'Paid Expense',
reserve: 'Outstanding Reserve',
totalIncurred: 'Total Incurred',
},
});
// lossRun.years[].claims[] — structured claim-level data
// lossRun.years[].totals — annual summary

PDF loss runs are rendered to images and processed by the AI extraction bridge:

import { extractFromPDF } from '@openinsure/loss-runs';
const lossRun = await extractFromPDF(pdfBuffer, {
carrier: 'Acme Mutual',
policyNumber: 'GL-2022-001234',
});
// Same output structure as CSV extraction
// extractionConfidence: 0.0–1.0 (below 0.75 → manual review)

Once extracted, the analyzeLossRun() function produces a 5-year summary:

import { analyzeLossRun } from '@openinsure/loss-runs';
const analysis = analyzeLossRun(lossRun, {
earnedPremium: [180_000, 195_000, 210_000, 225_000, 240_000], // 5 years
});
// analysis.lossRatioByYear: [0.42, 0.38, 0.61, 0.35, 0.44]
// analysis.fiveYearLossRatio: 0.44
// analysis.largestSingleClaim: 95_000
// analysis.openClaimsCount: 2
// analysis.trendDirection: 'STABLE' | 'DETERIORATING' | 'IMPROVING'
// analysis.flags: [...]

The analyzer automatically generates flags based on the loss history:

FlagSeverityTrigger
HIGH_LOSS_RATIOCritical5-year loss ratio > 70%
DETERIORATING_TRENDWarningLoss ratio increased ≥ 15 points in last 2 years
OPEN_CLAIMSWarning≥ 2 open claims at time of submission
CATASTROPHIC_LOSSCriticalSingle claim > 50% of annual premium
FREQUENCYWarning> 3 claims per year (small commercial)
RAPID_RESERVE_DEVELOPMENTWarningOpen reserves grew > 40% since last loss run
CARRIER_NONRENEWALInfoCurrent carrier did not offer renewal
analysis.flags.forEach((flag) => {
console.log(flag.code); // 'HIGH_LOSS_RATIO'
console.log(flag.severity); // 'CRITICAL'
console.log(flag.description); // '5-year loss ratio of 84% exceeds 70% threshold'
console.log(flag.year); // 2024 (if year-specific)
});

Flags are surfaced in the UW queue with color-coded badges and require acknowledgment before an underwriter can bind the policy.


For policyholders requesting their own loss run (to submit with a new application elsewhere), the generateLossRun() function produces a formatted loss run from OpenInsure’s claim records:

import { generateLossRun } from '@openinsure/loss-runs';
const lossRunPDF = await generateLossRun({
policyId: 'pol_01J8...',
yearsRequested: 5,
asOfDate: '2025-06-01',
requestedBy: 'Acme Roofing LLC',
format: 'pdf', // 'pdf' | 'csv' | 'json'
});
Terminal window
POST /v1/policies/:id/loss-run
Authorization: Bearer <token>
Content-Type: application/json
{
"yearsRequested": 5,
"asOfDate": "2025-06-01",
"requestedBy": "Acme Roofing LLC",
"format": "pdf"
}

The generated loss run is stored in R2 and a download link is returned. Producers can also trigger this from the producer portal via Policy → Request Loss Run.


Loss run analysis results automatically populate the submission’s Risk Scorecard in the Underwriting Workbench:

  • Loss ratio by year (bar chart)
  • Trend indicator (arrow + color)
  • Flag summary with drill-down to individual claim data
  • Comparison to program benchmark loss ratio