The exposure workbook is the hardest document in a casualty submission — and the most valuable. Payroll by class, vehicle schedules, SOVs, subcontractor lists, contractor work-in-progress by region: hundreds of rows across many sheets, in a layout every broker builds differently. This is exactly where template OCR and “LLM, read this spreadsheet” both fall over. Here’s how InsightXtract reads it — deterministically, every sheet, nothing dropped.

Why workbooks break naive extractors

  • A metadata block above a table — Insured / Policy Period / Total Payroll sitting one blank row above a wide exposure grid. Zone-splitters collapse the two into a single 2-column mess.
  • Stacked group headers — a row of merged group labels (“Current Estimate”, “Inception-to-Date”, “Backlog”) over a row of sub-columns. The sub-header row gets mistaken for data.
  • Multiple tables per sheet, sub-tables (vehicle-type counts, rented-auto counts), and per-region sheets (MIQ / WEST / SOUTH…).
  • Broker-specific wording — “Total Payroll” vs “Payroll”, “GL Exposure” vs “General Liability”.

Step 1 — Classify every sheet, every region

InsightXtract loads every sheet, splits it into regions on blank rows, and classifies each region as a key-value metadata block or a table (single- or stacked-header). Each detected table is mapped to a typed schedule by header name. A real operations workbook maps like this:

Sheet
Mapped schedule
Rows
General_liability
general_liability
52
workers_compensation
workers_compensation
150
automobile
automobile
66
driver_list
driver_list
64
named_insured_mix
named_insured_mix
46
location_list
location_list · construction_types
36
Operations
summary (key-value block)

Every sheet is surfaced — even a sub-table the base schema didn’t anticipate is emitted under its sheet name, so no data is silently lost.

Step 2 — Flatten stacked group headers

The contractor WIP workbook has 400 project rows per region under grouped headers. InsightXtract detects the two-row header, forward-fills the merged group labels, and flattens each leaf column to group — sub — turning an unreadable grid into clean, typed columns:

Raw (2-row merged header)
[ Current Project Estimate    ][ Backlog        ]
Revenue | Cost | GP (Loss) | ... | Revenue | ...
17,451,000 | 14,497,639 | ...
Flattened leaf columns
current_project_estimate_revenue
current_project_estimate_cost_of_revenues
current_project_estimate_gross_profit_loss
backlog_revenue ...

This is a heuristic, not a per-file rule — any grouped-header workbook flattens the same way, whatever the groups are named.

Step 3 — Type & normalize the columns

Each schedule column is bound to a governed vocabulary, so raw broker wording lands as canonical codes and out-of-vocabulary values are flagged:

DivisionStateWC CodeDescriptionRevenuePayroll
MIQLA us_state_codes91583 ncci_wc_class_codesMasonry$31,131,000$5,345,945
WESTCA95410Plumbing – Commercial$18,470,000$3,112,600

Bindings: state → us_state_codes, class → ncci_wc_class_codes, vehicle type → vehicle_body_types, occupancy → occupancy_types, construction → construction_types. And because column resolution is fuzzy, a rule written for payroll still binds a broker’s “Total Payroll” column.

Step 4 — Derive header exposures

Rating happens on totals, not rows. The document type declares deterministic sum / count / group_by rules that compute over the extracted rows — exact, reproducible, and traceable. From this workbook:

$177,816,199
GL payroll
∑ general_liability.payroll (52)
$245,751,000
WC payroll
∑ workers_compensation.payroll (150)
66
Autos
count(automobile)
$45.79B
Project revenue (all regions)
∑ *.current_project_estimate_revenue

The last is a cross-region wildcard sum — across all seven WIP sheets (1,330 projects) — so it stays right no matter how many regions the workbook holds. No LLM arithmetic anywhere.