• contact@verticalserve.com
Home / Engineering / Post 105
Engineering Blog · Post #105

Six Signal Types and One Score: How Claims Drive Submission Priority Without a New Scoring System

From "the org has an internal claims database with everything we need — open severity, recent reserves, litigation, frequency anomalies, NAICS-peer spikes, loss ratios — but a UW reviewing a renewal at 9am has no read on whether any of that should affect this submission's priority today" to "six claims signal types written into the same Priority Signal table the intelligence and news modules use, a per-LOB threshold table, an idempotent same-day scan, a four-band decision panel that aggregates the signals into low / medium / high / extreme, a hourly book sweeper, and zero changes to the existing Composite Urgency Scoring" — through one new threshold table, one service, six signal types, and a panel that drops in anywhere a UW makes a claims-aware decision.


The Problem

The org owns its own claims data. Claim rows have claim status (open / closed / reopened), total reserve, total incurred, litigation status, severity (low / medium / high / catastrophic), lob, opened/closed dates, the works. Years of history, a few hundred thousand rows, fully accessible.

What that data doesn't do today: change the priority of a submission Sarah is looking at right now.

Sarah is reviewing Acme Construction's renewal. The submission has been in queue for 12 days. Its Composite Urgency Score (blog #88) is computed from intake age, broker tier, loss-ratio band on the prior policy, and a handful of other signals — none of which know that Acme has three open high-severity claims that came in over the last 45 days, or that there's a litigation matter on the prior policy that just opened, or that NAICS 23 peer construction firms are seeing a frequency spike this quarter.

Two naïve framings:

  • Build a separate "claims priority" score. Now the workstation has two priority dimensions: Composite Urgency (no claims) and Claims Priority. UWs have to look at both. Triage queues sort by which one? Both? Neither?
  • Re-architect Composite Urgency Scoring to ingest claims directly. Now the scorer takes a dependency on the claims store, owns claims-specific thresholds, knows about LOB-specific tuning, and has a much harder unit-test surface. Every time the claims data changes, the scorer changes.

The right framing borrows from the multi-adapter framework (blog #104): decisioning signals are a separate concern from decisioning logic. Detection writes signals; the scorer reads them. Add a signal type, the scorer picks it up; remove it, the scorer doesn't notice.

Priority Signal already exists. The intelligence module (cap #1) writes recent capital raise, executive turnover recent, producer changed firm, license status change. The news module (cap #2) writes news bankruptcy, news litigation material, news breach or incident, news ma event, news layoff material, news regulatory action. Composite Urgency consumes all of these without knowing what produced them.

Claims should write the same way.

The InsightUW Approach

graph TD subgraph Claims["Claim store"] Open["claim status='open'<br/>+ severity ≥ floor"] Recent["opened within<br/>recent open days"] LIT["litigation status='open'<br/>+ severity ≥ floor"] Freq["claim count last 12mo<br/>vs. prior 12mo"] LR["incurred / written premium<br/>per loss period"] Naics["peer claim count last N mo<br/>at Naics prefix peers"] end subgraph Service["uw claims priority service"] Scan["scan for insured<br/>(idempotent same-day)"] Sweep["sweep active book<br/>(hourly)"] Panel["get decision panel<br/>(read-only)"] Thresh["Claims Priority Threshold<br/>(Builtin ← default ← LOB)"] end subgraph Sig["Priority Signal"] S1["claim open high severity"] S2["claim recent open at insured"] S3["claim frequency anomaly"] S4["claim naics peer spike"] S5["claim litigation open"] S6["claim loss ratio high"] end subgraph Consumers["Consumers"] CUS["Composite Urgency Scoring<br/>(blog #88, unchanged)"] CDP["claims-decision-panel<br/>(insured-360 + quote workspace)"] NOT["Notification<br/>('Intelligence' tab)"] end Open --> Scan Recent --> Scan LIT --> Scan Freq --> Scan LR --> Scan Naics --> Scan Thresh --> Scan Scan --> S1 Scan --> S2 Scan --> S3 Scan --> S4 Scan --> S5 Scan --> S6 Sweep --> Scan S1 --> CUS S2 --> CUS S3 --> CUS S4 --> CUS S5 --> CUS S6 --> CUS S1 --> CDP S2 --> CDP S3 --> CDP S4 --> CDP S5 --> CDP S6 --> CDP S1 --> NOT S5 --> NOT S3 --> NOT Scan --> Panel Panel --> CDP

One new table. One service. Six signal writers. The scorer doesn't change.

Six signal types — what each one detects

Each is one query. Each writes one signal row with severity (low/medium/high/critical) + payload summary (compact human string for UI) + evidence guid (FK back to the underlying claim where relevant).

The threshold table — one row per LOB, with cascading defaults

Resolution layers BUILTIN_THRESHOLDS ← default-row ← LOB-specific row, same pattern as detection rules in BOR (blog #106) and news preferences (cap #3). Admin tunes per-LOB; nothing else changes.

scan for insured — idempotent same-day

upsert same day writes one signal per (insured_id, signal_type) per calendar day. Re-running the scan five times in one day produces six signal rows total, not 30. Composite Urgency reads the latest; same-day repeats don't double-count.

get decision panel — read-only aggregation

The panel doesn't compute new things. It reads the signals already written + the underlying claim counts and returns:

Score is bounded [0, 1] (weights sum to 1, severity multipliers ≤ 1). Bands are the UI affordance: green / yellow / orange / red. The Composite Urgency score doesn't read this directly — it reads the signal rows. The decision panel is for the human.

sweep active book — the hourly path

Hourly, batch-bounded, idempotent. The default 100-batch / hour means 2400 insureds/day — comfortably more than the active book at most carriers. Tunable via CLAIMS_PRIORITY_BATCH_SIZE and CLAIMS_PRIORITY_INTERVAL_SECONDS; disabled via CLAIMS_PRIORITY_SWEEPER_DISABLED=1 for environments where claims-write is bursty.

Notifications — the three high-signal types

Three signals fire notifications by default (the others stay silent in the score, audible in the panel):

These land under the existing "Intelligence" inbox tab — same shelf as enrichment + news notifications. The UW doesn't get a separate "Claims" inbox; the priority story stays unified.

What the existing scorer doesn't do

The Composite Urgency Scoring service stays exactly as it was. It already reads Priority Signal rows (per blog #88). New signal types are just new strings in the database; the scorer's per-signal-type weighting table picks them up via config, not code.

This is the single most important property of the design: adding a signal source doesn't fork the scorer.

Worked Example: Sarah's Acme Renewal — Continued

Sarah opens Acme's renewal submission. The header now shows:

  • Composite Urgency: medium-high (was medium yesterday)
  • Claims decision band: high (orange pill)

She clicks the claims-decision-panel that's embedded under the header.

Decision band: HIGH (score 0.58).

Signal Severity Contribution Summary
claim_open_high_severity medium 0.125 2 open · reserve $710,000
claim_recent_open_at_insured medium 0.075 2 opened in last 30d
claim_litigation_open high 0.1125 1 (severity=high) — opened 18d ago
claim_loss_ratio_high high 0.1125 71% — 3-yr
claim_naics_peer_spike medium 0.05 7 NAICS-23 peer claims last 6mo
claim_frequency_anomaly medium 0.075 9 last-12mo / 5 prior-12mo (1.8×)

She clicks the litigation row. The panel drills into the underlying Claim row — claim #CLM-2026-0419, opened 18 days ago, severity high, litigation status open, plaintiff demand $4.2M, attached policy guid resolves to the prior bound policy on this account. She reads the litigation summary, the carrier reserve note, and the latest litigation update.

She clicks the frequency anomaly row. Panel shows the 9 last-12mo claims by LOB (5 GL, 3 Auto, 1 Property), each with a one-line summary. Two of the GL claims are still open and feeding the open-high-severity signal — the signals overlap but don't double-count because the score weights are independent and the severity is computed per-row.

She switches to the inbox. Three notifications under "Intelligence":

  • claim severity signal · Acme Construction · 2 open · reserve $710K · 38m ago
  • claim litigation alert · Acme Construction · 1 open · high severity · 38m ago
  • claim frequency anomaly · Acme Construction · 1.8× spike · 38m ago

All three were emitted from the same scan for insured call when the sweeper hit Acme this morning. She acknowledges all three; their navigation lands her back on the decision panel each time.

She opens the prior-policy T&Cs diff (blog #103, customer-360). She sees deductible up from $250K to $500K, premium up 22.5%. The renewal pricing already reflects the loss experience. She makes the renewal-bind decision; the audit trail captures the claims decision band, the signal contributions, and her referral notes.

The next morning, the sweeper runs again. Three of the signals re-emit (still material), one (frequency_anomaly) doesn't because the prior-12mo window rolled and the ratio is now 1.4× (under the 1.5× threshold). Acme's claims band drops back to "medium." Composite Urgency drops one notch. The priority badge ticks down. The system tracks the slope of the risk; Sarah didn't have to.

When the sweeper doesn't help

A different submission lands Monday morning — Beta Industries, brand new, no prior policies, no claim history. scan for insured runs. Six queries, six negative answers. Zero signals written. The panel renders: "No claims signals on file. Decision band: low (0.0)."

The score isn't artificially zeroed; it is genuinely zero. The UI says so. The UW reads it as "no claims-side risk we know of, look at exposure + intelligence + financials instead" and keeps moving.

What's Deferred (Phase 2)

  • Claim severity inferred from incurred when severity field is null. Today, Claim.severity must be set explicitly. A heuristic from total incurred magnitude + lob is a follow-up; works around stale claims data where severity wasn't categorized at intake.
  • Cross-account aggregation across linked entities. Acme has 3 subsidiaries on separate Insured rows. Today, scan for insured is per-row; rolling up by parent insured group is a Phase 2 join. Helps for large captive-style structures.
  • Claims trend prediction. The 6 signals are point-in-time. A trend signal — "loss ratio rising 8pp/yr over 3yr" — is mechanical to add but needs more careful threshold selection. Defer until book maturity supports the calibration.
  • Per-loss-period loss ratio. Today, claim loss ratio high uses the 3-yr rolling ratio. A per-policy-year loss ratio (matched to Policy Year) is a finer signal but needs the policy-year join in the hot path.
  • Auto-decline based on signal threshold. Today, signals affect priority. They don't auto-decline. The Auto Declination feature (separate module) consumes Priority Signal too; a rule that combines two-or-more critical claims signals into auto-decline is one rule row, no new code.
  • Reserve adequacy second opinion. Today, the reserve threshold is the writer's reserve. A second-opinion adequacy adjustment using actuarial loss-development factors is deferred — the scorer doesn't need to second-guess reserves to do its job.

What This Means for Underwriters

  1. Claims data participates in priority without a new score. Composite Urgency Scoring stays canonical; claims signals contribute via the same Priority Signal table the intelligence and news modules use.
  2. Six signals cover the high-value cases. Open severity, recent open, frequency anomaly, NAICS peer spike, litigation open, loss ratio high. Each is one query; each is one row.
  3. Same-day idempotency means re-scanning is free. Scanning Acme five times during the day produces at most six signal rows, not 30. UWs can hit "Refresh" without polluting the scorer.
  4. Per-LOB thresholds. Construction GL has different open-severity bars than D&O has different reserves than Cyber has different litigation cadence. The threshold table cascades BUILTIN ← default ← LOB.
  5. The hourly sweeper keeps the active book current. UWs see the right priority when they sit down at 9am; they don't have to re-trigger anything.
  6. The decision panel is read-only. Underwriters see the band, the contributing signals, the underlying claims drill-down. They don't get a "tune" button — that's the threshold admin's job.
  7. Notifications fire on the three high-signal types. Severity, litigation, frequency anomaly. The other three (recent open, peer spike, loss ratio) move the score but stay silent on inbox to avoid notification fatigue.
  8. Brand-new insureds get an honest zero. No claim history = no signals = score 0.0 = "low" band. The system doesn't fake-color the absence of data.
  9. The scorer reads from one table; the panel reads the same table plus underlying claims. The two views can never diverge — they're computed from the same rows.
  10. Adding a new signal type is one new query + one new entry in the score-weights JSON. No scorer change, no migration, no UI change beyond the optional inclusion in the panel.

What's Next

BOR conflict detection (blog #106) reuses the same "detect → write → notify → resolve" shape but the entity is a conflict (a state machine), not a signal. The framework patterns recur; the workflow is richer.


Want to see how InsightUW lets your claims database raise the priority bar without writing a new scorer? Request a demo.

See InsightUW run on your data

A 45-minute working session with a real broker email and your LOBs.

Request a demo