Claims Management
The @openinsure/claims package manages the complete claims lifecycle from First Notice of Loss (FNOL) through final closure. The module integrates with the billing system for reserve accounting, the document system for attachment management, and the compliance engine for state-specific handling requirements.
Claims Lifecycle
FNOL Filed
│
▼
┌─────────────┐
│ open │ ◄── Assignment, initial contact, coverage verification
└──────┬──────┘
│
▼
┌─────────────┐
│investigating│ ◄── Field investigation, recorded statements, expert engagement
└──────┬──────┘
│
┌────┴────────────────────────┐
│ │
▼ ▼
┌──────────┐ ┌────────────┐
│ reserved │ │ litigated │ ◄── Suit filed, defense counsel assigned
└──────┬───┘ └──────┬─────┘
│ │
▼ ▼
┌──────────────┐ ┌────────────┐
│ in_settlement│ │ in_defense │
└──────┬───────┘ └──────┬─────┘
│ │
└──────────┬──────────────┘
▼
┌──────────┐
│ settled │ ◄── Settlement agreement executed
└──────┬───┘
│
▼
┌──────────┐
│ closed │ ◄── All payments disbursed, file closed
└──────────┘
At any stage:
─── denied ──► closed (coverage denied)
FNOL — First Notice of Loss
Claims can be filed via the API, the policyholder portal, the producer portal, or by an adjuster directly.
File a Claim
POST /v1/claims
Authorization: Bearer <token>
Content-Type: application/json
{
"policyId": "pol_01J8...",
"dateOfLoss": "2025-06-15T14:30:00Z",
"reportedAt": "2025-06-15T18:00:00Z",
"lossType": "BODILY_INJURY",
"lossCause": "SLIP_AND_FALL",
"lossDescription": "Customer slipped on wet floor in lobby. Treated at ER.",
"estimatedLoss": 45000,
"claimant": {
"name": "Jane Smith",
"phone": "802-555-1234",
"address": "123 Main St, Burlington, VT 05401"
},
"location": {
"address": "456 Commerce Dr, Burlington, VT 05401",
"description": "Lobby entrance"
}
}
Response:
{
"id": "clm_01J8...",
"claimNumber": "GL-2025-000042",
"status": "open",
"policyId": "pol_01J8...",
"assignedAdjusterId": "adj_01J8...",
"dateOfLoss": "2025-06-15T14:30:00Z",
"coverageVerification": {
"policyInForce": true,
"coverageApplies": true,
"reservedAmount": 45000,
"sublimitApplicable": null
}
}
Assignment Rules
Claims are auto-assigned based on:
- Line of business — GL claims go to GL-licensed adjusters.
- Geography — Prefer adjusters licensed in the loss state.
- Workload — Round-robin among available adjusters below the workload threshold (configurable, default 35 open claims).
- Severity — Claims above $100,000 are assigned to senior adjusters.
- Manual override — Supervisors can reassign at any time.
Reserve Management
Case Reserves
Every open claim has a case reserve — the adjuster's best estimate of the ultimate loss and expense to settle the claim.
POST /v1/claims/:id/reserves
Authorization: Bearer <adjuster_token>
Content-Type: application/json
{
"indemnityReserve": 35000,
"expenseReserve": 12000,
"rationale": "ER bills confirmed at $8,200. Ongoing PT likely. Attorney involved."
}
Reserve changes are logged to claim_reserve_history — no in-place updates. The audit trail shows every reserve movement with adjuster, date, amount, and rationale.
Reserve Adequacy Flags
The system automatically flags reserves that appear inadequate:
| Flag | Trigger |
|---|---|
ROUND_NUMBER | Reserve is an exact round number (suggests placeholder) |
RAPID_INCREASE | Reserve increased >50% in 30 days |
EXCEEDED_ESTIMATE | Paid-to-date + reserve exceeds original estimate by >25% |
APPROACHING_POLICY_LIMIT | Reserve exceeds 80% of applicable policy limit |
IBNR_OUTLIER | Claim's development pattern is 2+ standard deviations from cohort |
IBNR Reserves
Incurred But Not Reported (IBNR) reserves are calculated at the organization level by the actuarial module. OpenInsure provides:
- Chain Ladder method for standard lines
- Bornhuetter-Ferguson method for immature lines with limited data
- Manual IBNR entry for actuary override
GET /v1/analytics/:orgId/ibnr?asOf=2025-06-30
# Returns:
{
"asOfDate": "2025-06-30",
"method": "CHAIN_LADDER",
"ibnrByLine": {
"GL": { "ibnr": 285000, "confidence": "HIGH" },
"CYBER": { "ibnr": 140000, "confidence": "MEDIUM" }
},
"totalIBNR": 425000
}
Subrogation Workflow
When a third party is responsible for the loss, the claims module tracks subrogation recovery.
POST /v1/claims/:id/subrogation
Authorization: Bearer <adjuster_token>
Content-Type: application/json
{
"potentialRecovery": 28000,
"responsibleParty": {
"name": "Green Valley Maintenance LLC",
"insurer": "Acme Insurance",
"claimNumber": "ACI-2025-887621",
"adjusterContact": "bob.jones@acmeins.com"
},
"subroStatus": "DEMAND_SENT",
"demandAmount": 35000,
"demandDate": "2025-08-01"
}
Subrogation status codes:
| Status | Description |
|---|---|
IDENTIFIED | Third party identified, evaluating recovery potential |
DEMAND_SENT | Demand letter sent to third party or their insurer |
NEGOTIATING | Counter-offer received, negotiating |
RECOVERED | Full or partial recovery received |
CLOSED_NO_RECOVERY | Recovery not pursued or pursuit exhausted |
Litigation Tracking
When a claimant files suit, the claim transitions to litigated status and triggers defense counsel assignment.
POST /v1/claims/:id/litigate
Authorization: Bearer <adjuster_token>
Content-Type: application/json
{
"suitFiledDate": "2025-07-20",
"courtJurisdiction": "Chittenden County Superior Court, VT",
"caseNumber": "23-CV-0481",
"defensePanel": "auto", // use program's panel counsel, or provide counsel_id
"trialDate": "2026-03-15"
}
Litigation features:
- Defense billing management — Counsel submits invoices via the portal, adjuster approves against litigation budget.
- Diary management — Automated reminders for deposition deadlines, discovery cutoffs, mediation dates.
- Coverage position letters — Template-driven reservation of rights and coverage denial letters.
- Verdict/settlement tracking — Record outcome, final payment amounts, and close litigation file.
Settlement Negotiation
POST /v1/claims/:id/settle
Authorization: Bearer <adjuster_token>
Content-Type: application/json
{
"settlementAmount": 27500,
"expenseAmount": 9800,
"claimantName": "Jane Smith",
"releaseType": "GENERAL_RELEASE",
"payeeName": "Jane Smith",
"payeeAddress": "123 Main St, Burlington, VT 05401",
"paymentMethod": "CHECK"
}
Settlements above the adjuster's authority level require supervisor approval:
| Adjuster Level | Authority Limit |
|---|---|
| Associate | $10,000 |
| Adjuster II | $25,000 |
| Senior Adjuster | $75,000 |
| Supervisor | $250,000 |
| Manager | Unlimited (carrier authority applies) |
Payment Disbursement
Once a settlement is approved, payment is disbursed via the billing module:
POST /v1/claims/:id/payments
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"paymentType": "SETTLEMENT",
"amount": 27500,
"payeeType": "CLAIMANT",
"payeeName": "Jane Smith",
"paymentMethod": "CHECK",
"memo": "Settlement of claim GL-2025-000042 per release dated 2025-09-01"
}
Payment types:
| Type | Description |
|---|---|
SETTLEMENT | Final settlement payment to claimant |
MEDICAL | Medical expense payment (direct to provider) |
DEFENSE_EXPENSE | Defense counsel invoice payment |
EXPERT_EXPENSE | Expert witness, IME, or appraiser fees |
SUBROGATION_RECOVERY | Recovery received from third party (negative — income) |
Claim Closure
POST /v1/claims/:id/close
Authorization: Bearer <adjuster_token>
Content-Type: application/json
{
"closureReason": "SETTLED",
"finalPaidIndemnity": 27500,
"finalPaidExpense": 9800,
"closingNotes": "Settled for $27,500. Release executed 2025-09-01. File closed."
}
On closure, the system:
- Sets all reserves to zero (closed with final payments known).
- Posts the final paid amounts to the loss run.
- Updates the organization's loss ratio statistics.
- Triggers a bordereaux update for claims reporting.
Document Management
See the Claims Attachments API for file upload and retrieval. Supported types: PDF, JPEG, PNG, TIFF, DOCX. Max 10 MB per file.
POST /v1/claims/:id/attachments
Content-Type: multipart/form-data
file=@police_report.pdf
documentType=police_report
GET /v1/claims/:id/attachments
# Returns: { data: Document[], total: number }
Salvage Recovery
When a total loss or significant property claim results in recoverable assets, the salvage module tracks the asset from identification through disposition and recovery. The API is at /v1/claims/:id/salvage, implemented in apps/api/src/routes/claims/salvage.ts.
Creating a Salvage Record
POST /v1/claims/:id/salvage
Authorization: Bearer <adjuster_token>
Content-Type: application/json
{
"itemDescription": "2023 Freightliner Cascadia — cab and chassis",
"estimatedValue": 18000,
"disposition": "pending",
"storageLocation": "ABC Towing, 1234 Industrial Blvd, Houston TX",
"storageStartDate": "2025-07-01T00:00:00Z",
"dailyStorageCost": 45,
"notes": "Title held. Awaiting auction date."
}
Disposition Statuses
| Status | Description |
|---|---|
pending | Salvage identified, not yet disposed |
retained_by_insured | Insured retains the asset (salvage deduction applied to settlement) |
sold_at_auction | Asset sold through salvage auction |
scrapped | Asset scrapped with no meaningful recovery |
donated | Asset donated (tax benefit may apply) |
transferred | Asset transferred to another party |
Updating Salvage (Recording Recovery)
When salvage is disposed, update the record with actual recovery details:
PATCH /v1/claims/:id/salvage/:salvageId
Authorization: Bearer <adjuster_token>
Content-Type: application/json
{
"actualRecovery": 14200,
"disposition": "sold_at_auction",
"buyerName": "Southwest Auto Salvage",
"saleDate": "2025-08-15T00:00:00Z",
"titleTransferred": true
}
Updatable fields include:
| Field | Description |
|---|---|
actualRecovery | Dollar amount recovered from disposition |
disposition | New disposition status |
buyerName | Name of buyer (auction or private sale) |
saleDate | Date of sale or transfer |
titleTransferred | Whether vehicle/asset title has been transferred |
storageLocation | Updated storage location |
dailyStorageCost | Updated daily storage rate |
Deleting Salvage Records
Only records in pending disposition can be deleted. Non-pending records return HTTP 409:
DELETE /v1/claims/:id/salvage/:salvageId
Authorization: Bearer <adjuster_token>
Audit Trail
All salvage operations generate entries in the claim_events table:
| Event Type | Trigger |
|---|---|
salvage_created | New salvage record created |
salvage_updated | Salvage record modified (disposition change, recovery recorded) |
salvage_removed | Pending salvage record deleted |
Each event records the actor user ID, role, and relevant details (salvage ID, item description, change set).
Storage Cost Tracking
For assets in physical storage (tow yards, warehouses), the system tracks:
storageLocation-- Facility name and addressstorageStartDate-- Date storage begandailyStorageCost-- Per-day rate
This data supports storage cost accrual and helps adjusters make timely disposition decisions to minimize holding costs.
Total Loss Workflow
When a vehicle or asset is damaged beyond economical repair, the total loss module manages the determination, settlement calculation, and title disposition. The API is at /v1/claims/:id/total-loss, implemented in apps/api/src/routes/claims/total-loss.ts.
Total Loss Determination
POST /v1/claims/:id/total-loss
Authorization: Bearer <adjuster_token>
Content-Type: application/json
{
"reason": "damage_exceeds_value",
"actualCashValue": 32000,
"deductible": 1000,
"priorDamage": 500,
"ownerRetainsSalvage": false,
"lienholderName": "Capital One Auto Finance",
"lienholderPayoff": 18000
}
Only one total loss record is allowed per claim. Attempting to create a second returns HTTP 409.
Total Loss Reasons
| Reason | Description |
|---|---|
damage_exceeds_value | Repair cost exceeds the vehicle's actual cash value (most common) |
constructive_total_loss | Repair cost exceeds the state's total loss threshold (typically 75-80% of ACV) |
theft_unrecovered | Vehicle stolen and not recovered within the statutory period |
regulatory | State regulation mandates total loss determination |
Settlement Calculation
The system automatically calculates the settlement amount:
Settlement = ACV - Deductible - Prior Damage - Salvage Deduction (if owner retains)
- ACV (Actual Cash Value) -- Fair market value of the vehicle immediately before the loss.
- Deductible -- Policy deductible amount.
- Prior Damage -- Deduction for pre-existing unrepaired damage.
- Salvage Deduction -- Applied only when
ownerRetainsSalvageistrue. Represents the salvage value deducted from the settlement so the insured can keep the vehicle.
The result is floored at zero (settlement cannot be negative).
Title Status
The system determines the title status based on the total loss details:
| Title Status | Condition |
|---|---|
clear | No lienholder and owner does not retain salvage |
lienholder_payoff_pending | Lienholder payoff amount specified |
retained_by_owner | Owner retains salvage (title branded as salvage/rebuilt) |
title_transferred | Title has been transferred to the carrier or salvage buyer |
Updating Total Loss Records
After the initial determination, the title status and settlement can be updated as the process progresses:
PATCH /v1/claims/:id/total-loss
Authorization: Bearer <adjuster_token>
Content-Type: application/json
{
"titleStatus": "title_transferred",
"settledAt": "2025-08-20T00:00:00Z"
}
Updatable fields: titleStatus, settledAt, lienholderName, lienholderPayoff.
Audit Trail
Total loss operations generate events in the claim_events table:
| Event Type | Trigger |
|---|---|
total_loss_determined | Initial total loss determination created (includes reason, ACV, settlement amount, title status) |
total_loss_updated | Total loss record modified (title transfer, settlement date, lienholder changes) |
Authorization
All salvage and total loss endpoints require one of: adjuster, underwriter, org_admin, or superadmin role. Write operations (create, update, delete) exclude superadmin from the write path -- only adjuster, underwriter, and org_admin can modify records.