@openinsure/rating computes gross premium from a structured set of factors stored in the rate_tables database table. This page documents the schema, how the waterfall is applied, how to seed demo data, and how to use the interactive rater in the Underwriting Workbench.
Database Schema
Rate tables are stored in therate_tables table in PlanetScale, defined in packages/db/src/schema/rate-tables.ts.
base_rates, factor_tables, rules, class_codes, and territory_factors for efficient JSONB path queries.
JSONB Column Shapes
base_rates
State-keyed map of base loss costs. The fallback key __ is used when no state-specific rate exists.
perUnit is in dollars per exposure unit. The rating engine calls toCentsPerUnit() internally before any arithmetic to avoid floating-point drift.
basis controls how exposure is measured:
| Value | Exposure unit |
|---|---|
revenue | Per $1,000 gross revenue |
payroll | Per $100 payroll |
value | Per $100 insured value |
employees | Per employee |
class_codes
Array of class code definitions with rate modifiers:
territory_factors
State/territory multiplicative factors applied after the class code modifier:
minimum_premiums
State-keyed minimum premium floor (in dollars). Applied after the full factor waterfall:
factor_tables
Arbitrary named factor tables for LOB-specific adjustments (ILFs, schedule credits, deductible factors):
Rating Engine Waterfall
The engine is implemented inpackages/rating/src/ with the explainability layer in explain.ts.
Factor Application Order
-
Loss cost lookup (
getLossCost()inloss-costs.ts)- Looks up
base_rates[state](orbase_rates['__']as fallback). - Converts
perUnitdollars to cents viatoCentsPerUnit(). - Multiplies by the exposure units to produce
basePremium.
- Looks up
-
Territory factor — multiplies
basePremiumbyterritory_factors[state]. -
Class code modifier — multiplies by the matching
class_codes[].modifier. -
Schedule modifier — manual credits/debits applied by the underwriter (bounded by
factor_tables.schedule_credit_max). -
Experience mod — actuarial modification based on loss history (
experience-mod.ts). - LOB-specific factors — line-of-business engine factors (telematics score, FMCSA safety rating, CSA score, etc.).
-
AI risk adjustment — optional ±5 % adjustment from
computeAiRiskNarrative()when signals exceed threshold. -
Expense loading —
1 / permissibleLossRatiowhere PLR is set per program. - State premium tax — applied as a surcharge after the base premium.
-
Minimum premium floor — result is raised to
minimum_premiums[state]if below floor.
Explainability (explainRating)
Every quote can be decomposed into per-factor dollar contributions for underwriter transparency and FCRA adverse action notices:
Seeding Demo Data
Demo rate tables are inpackages/db/seeds/demo-data.sql. Run the seed against your local or branch PlanetScale database:
- Commercial Auto (
commercial_auto) — trucking class codes, state territory factors for TX/CA/FL/NY - General Liability (
general_liability) — revenue-based exposure, schedule credit table - Workers Compensation (
workers_comp) — NCCI class code structure, experience mod factors
Admin UI — Rate Table CRUD
Rate tables are managed in the Admin portal at/rate-tables. The UI provides:
- List view — all active rate tables for the org, grouped by line of business
- Detail view — full JSONB editors for each column with schema validation
- Version history — audit trail of all past versions linked to
program_id - Import — bulk-load rate tables from a structured JSON file
Import API
is_active = true while marking the previous version inactive.
:::note
Imports are org-scoped. You cannot import into a rate table that belongs to a different organization.
:::
Interactive Rater (Underwriting Workbench)
The Underwriting Workbench (apps/underwriting-workbench) includes an InteractiveRater component that calls the rating explain endpoint with live DB data.
- Override individual factors — adjust schedule modifier, select an alternate class code, or change the state without re-running the full submission flow.
- See real-time dollar impact — each change re-calls
/v1/rating/explainand updates the factor waterfall display. - Export the explanation — generates a PDF adverse action notice via
@openinsure/documents.
Rate Table Comparison
The platform provides two comparison capabilities: market benchmarking against industry data, and side-by-side diffing of two rate table versions.Market Benchmark Comparison
Compare a rated premium against market benchmark data for a given class code, state, and line of business:market_benchmarks table, then calls compareToMarket() from @openinsure/rating to produce a positioning result relative to the market distribution:
- medianRate — The 50th percentile market rate
- p25Rate — The 25th percentile (lower quartile)
- p75Rate — The 75th percentile (upper quartile)
premium, classCode, state, lob) are required. Returns 404 if no benchmark data exists for the combination.
Market Benchmark Data Management
Benchmarks are managed through two endpoints:lob, state, and classCode.
Rate Table Version Diff
Compare two rate table versions side-by-side to understand what changed between releases:| Section | What is compared |
|---|---|
baseRates | State-keyed base loss costs (perUnit, minPrem, basis) |
factorTables | Named factor tables (deductible factors, schedule credit limits) |
territoryFactors | State territory multiplicative factors |
rules | Rating rules and conditions |
conditionRules | Conditional rating logic |
key, the left value (from the first table), and the right value (from the second table). Only keys where the values differ are included.
The response also includes metadata for each side:
404 if one or both rate tables are not found. Both tables must belong to the authenticated organization.