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

Typed Referral Rules and Rule Sets: How a $14M Private D&O Quote Auto-Fires the Right Referral

From "the broker's been waiting two days because nobody knew this needed senior review" to "the moment Sarah clicks Send, the quote routes itself to the right manager with the right rationale" — through nine typed criterion evaluators, named rule packs with seasonal windows, and a rationale template that writes itself.


The Problem

In most underwriting platforms, "referral rules" are PDF tables. A new UW writes a $14M private D&O quote, doesn't notice it's $4M over their cap, sends it. The broker reads it as a binding offer. Two weeks later — after the senior UW spots it on a sample audit — the quote has to be retracted. Relationship damage, compliance scramble.

The half-baked solutions don't help:

  • Hardcoded if premium > X in the rating service. Works once, then needs a deploy every time the cap moves.
  • Free-text Jinja conditions in a rules table. Works for everything and nothing — the threshold for "loss ratio > 70%" lives next to the threshold for "industry NAICS prefix in [541110, 541330]" and they share zero structure.
  • One flat list of rules. Hurricane-season rules sit alongside year-round rules; deactivating "Bermuda — refer all quotes" for the offseason means flipping eight rows by hand and remembering to flip them back.

The result: rules land in code or in PDFs, but rarely in a place a UW manager can actually own.

The InsightUW Approach

InsightUW makes referral rules a first-class typed entity (Referral Rule) with nine criterion types you pick from a dropdown — no Jinja for thresholds, no hand-rolled comparators. Rules optionally roll up under named rule sets (Referral Rule Set) with effective windows, so seasonal packs auto-retire on a date.

graph TD subgraph Trigger["Trigger Points"] QS["Quote Send"] RG["Renewal Generation"] MAN["Manual Trigger"] WF["Workflow action='refer'"] end subgraph Sets["Rule Sets (cap #5)"] S1["BA Property — Hurricane Season<br/>Jun 1 → Nov 30"] S2["US D&O H1 2026<br/>always-on"] S3["Naics Restricted Classes<br/>always-on"] end subgraph Rules["Typed Rules"] R1["policy limit ≥ $10M<br/>(US D&O Private)"] R2["loss ratio ≥ 0.70<br/>(US Property renewals)"] R3["jurisdiction blanket<br/>(Bermuda)"] R4["authority exceeded<br/>(any LOB)"] R5["risk score ≥ 75<br/>(Property)"] R6["industry class in [541330]<br/>(D&O)"] R7["cat exposure pml ≥ $50M"] R8["large claim count ≥ 3"] R9["aggregate limit ≥ $25M"] end subgraph Engine["Evaluator + Resolver"] Eval["Criterion Evaluators<br/>(typed numerics, no Jinja)"] RES["target resolver:<br/>team manager / lob specialist /<br/>authority matrix lookup / named"] Dedup["Idempotency:<br/>(submission guid, rule guid)"] end subgraph Out["Outputs"] REF["Referral Queue row<br/>+ rationale template rendered"] Audit["Audit Entry"] Notif["Notification → routed manager"] end QS --> Eval RG --> Eval MAN --> Eval WF --> Eval S1 -.->|cascade| R3 S1 -.-> R7 S2 -.-> R1 S3 -.-> R6 R1 --> Eval R2 --> Eval R3 --> Eval R4 --> Eval R5 --> Eval R6 --> Eval R7 --> Eval R8 --> Eval R9 --> Eval Eval --> Dedup Dedup --> RES RES --> REF REF --> Audit REF --> Notif

The Nine Criterion Types

Every rule picks from one typed evaluator. Thresholds are numeric (or a JSON list for the few criteria that need it) — never freeform code.

Criterion type Threshold Example
policy limit dollars "≥ $10M for US D&O Private"
aggregate limit dollars "Σ active policies + this quote ≥ $25M for the insured"
premium dollars "≥ $1M annual premium"
loss ratio fraction (0–1) "≥ 0.70 on US Property renewals"
large claim count count "≥ 3 claims ≥ $1M in last 5 years"
authority exceeded derived UW's Authority Matrix.max_limit / max premium / max loss ratio exceeded
jurisdiction blanket none "Refer every quote in Bermuda"
risk score numeric "Insured.risk_score ≥ 75"
industry class in NAICS prefix list "NAICS starts with one of [541330, 541512]"
cat exposure pml dollars "Σ Location.pml_100yr ≥ $50M"

Rule Sets — Seasonal and Themed Packs

A Referral Rule Set carries an effective window. Member rules inherit it: a hurricane-season rule with effective_from=2026-06-01, effective_to=2026-11-30 on its parent set automatically stops firing November 30 — no flip, no calendar reminder, no scramble.

Set Window Members
BA Property — Hurricane Season Jun 1 → Nov 30 jurisdiction blanket (BA) · cat_exposure_pml ≥ $50M
US D&O H1 2026 always-on policy_limit ≥ $10M · industry_class_in [restricted NAICS]
Q3 Capacity Tightening Jul 1 → Sep 30 aggregate_limit ≥ $25M · premium ≥ $1M

Worked Example: Stronghold Capital — $14M Private D&O Quote

Sarah Chen (US underwriter, max_limit $7M for D&O) is finishing a Private D&O quote for Stronghold Capital — total limit $14M, premium $385k. She clicks Send Quote.

send quote hooks into the trigger pipeline:

  1. Build context — build context produces {insured: Stronghold Capital, lob: D&O, product: Private D&O, jurisdiction: US, requested_limit: 14000000, premium: 385000}.
  2. Scope rules — scoped rules finds three eligible: the seeded "US D&O Private — limit ≥ $10M" rule (under set "US D&O H1 2026"), "UW authority exceeded" (always-on), and "premium ≥ $1M" (active under "Q3 Capacity Tightening" — but it's April, so the set is out of window and the rule is skipped via cascade).
  3. Run typed evaluators_eval_policy_limit(observed=14000000, threshold=10000000) → fires. eval authority exceeded looks up Sarah's Authority Matrix row, sees max_limit=$7M, fires with breach $14M > $7M. The premium rule was already filtered out.
  4. Idempotency check — no prior open referrals for (stronghold_submission_guid, *). Both fires create new rows.
  5. Render rationale — the rule's rationale template is a Jinja string: "Quote {{ quote_number }} for {{ insured_name }} requests a {{ requested_limit | currency }} limit, which exceeds the US private D&O underwriting threshold of {{ threshold | currency }}." Renders to "Quote QT-2026-DO-0319 for Stronghold Capital requests a $14,000,000 limit, which exceeds the US private D&O underwriting threshold of $10,000,000."
  6. Resolve target — the rule's target_resolver=team_manager. Sarah's Team Member.team_idTeam.manager_name = "Jennifer Walsh". The authority-exceeded rule's resolver is authority matrix lookup — finds the smallest authority covering $14M for D&O, lands on Jennifer too.
  7. Persist — two Referral Queue rows, both referred_to=Jennifer Walsh, with full context json snapshot, criterion type set, audit row written.
  8. Notify — notify referral posts to Jennifer's inbox + email channel based on her Notification Preference.

Total time from Send click to Jennifer's bell badge: under a second. Sarah sees a banner: "This quote can't be sent — 2 referrals open. Open queue."

The Bermuda Twist

Capability #1 also seeded a jurisdiction blanket rule scoped to BA — every quote routes to a manager regardless of size. That same rule, dropped under the "BA Property — Hurricane Season" set with effective_from=Jun 1, only fires June through November. Outside the season, BA quotes go straight through. One toggle, four lines in the admin UI.

The Identification Mode

Capability #5 also exposes a POST /identify endpoint that returns the rule sets governing a submission without firing anything. The submission view's "Applicable rule sets" chip strip shows: BA Property — Hurricane Season · 2/3 firing — the manager sees set membership before deciding to commit. Useful when scoping coverage with a broker over the phone.

What This Means for Underwriters

  1. Rules live where managers can edit them. Admin UI at /uw/admin/referral-rules — pick criterion type from a dropdown, set the threshold number, save. No code, no PDF.
  2. Seasonal packs retire themselves. Hurricane-season rules turn off November 30 automatically. The set's effective to cascades to every member.
  3. Rationale writes itself. The Jinja template runs once, fills in the live merge data, lands on the manager's queue as a clear sentence — not a stack trace of which-rule-fired.
  4. Routing is opinionated. Four resolver strategies cover the realistic cases (team manager, LOB specialist, smallest authority that covers, named user). The fallback is the team-manager-of-originator so notifications never get stuck.
  5. Rules don't double-fire. Idempotency keyed on (submission_guid, rule_guid) — a UW who saves a quote twice doesn't double-trigger. Re-evaluating a manually edited quote picks up the change without flooding the queue.
  6. The audit is automatic. Every fire writes a Audit Entry and a Referral Queue.context_json snapshot. Compliance can reconstruct exactly what tripped, when, against what data.

What's Next

Next: Composite Urgency Scoring — Why a P2 Referral on a Quote Expiring in 6 Hours Outranks a P1 With No Deadline


Want to see how a single rule pack handles your hurricane-season referrals end-to-end? 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