> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gorules.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Insurance underwriting

> Build underwriting rules for risk assessment, premium calculation, and policy decisions.

export const DecisionGraphViz = ({nodes = [], edges = [], highlighted = null, title = null}) => {
  const nodeColors = {
    input: {
      bg: '#f8fafc',
      border: '#94a3b8',
      text: '#475569',
      icon: '→'
    },
    output: {
      bg: '#f8fafc',
      border: '#94a3b8',
      text: '#475569',
      icon: '←'
    },
    table: {
      bg: '#fefce8',
      border: '#d9c860',
      text: '#7c6f2a',
      icon: '▤'
    },
    expression: {
      bg: '#f5f3ff',
      border: '#a78bda',
      text: '#5b4a8c',
      icon: 'ƒ'
    },
    function: {
      bg: '#fdf2f8',
      border: '#e293b7',
      text: '#8c4a6d',
      icon: '{ }'
    },
    switch: {
      bg: '#f0fdfa',
      border: '#6bc4b8',
      text: '#3d7a72',
      icon: '◇'
    }
  };
  const nodeWidth = 140;
  const nodeHeight = 64;
  const horizontalGap = 80;
  const verticalGap = 24;
  const inDegree = {};
  const outEdges = {};
  nodes.forEach(n => {
    inDegree[n.id] = 0;
    outEdges[n.id] = [];
  });
  edges.forEach(e => {
    inDegree[e.to] = (inDegree[e.to] || 0) + 1;
    outEdges[e.from] = outEdges[e.from] || [];
    outEdges[e.from].push(e.to);
  });
  const queue = nodes.filter(n => inDegree[n.id] === 0).map(n => n.id);
  const nodeLevel = {};
  queue.forEach(id => nodeLevel[id] = 0);
  const tempInDegree = {
    ...inDegree
  };
  while (queue.length > 0) {
    const current = queue.shift();
    const currentLevel = nodeLevel[current];
    (outEdges[current] || []).forEach(next => {
      nodeLevel[next] = Math.max(nodeLevel[next] || 0, currentLevel + 1);
      tempInDegree[next]--;
      if (tempInDegree[next] === 0) queue.push(next);
    });
  }
  const levels = {};
  nodes.forEach(n => {
    const level = nodeLevel[n.id] || 0;
    levels[level] = levels[level] || [];
    levels[level].push(n);
  });
  const nodePositions = {};
  const numLevels = Object.keys(levels).length;
  const maxNodesInLevel = Math.max(...Object.values(levels).map(l => l.length), 1);
  const canvasHeight = Math.max(maxNodesInLevel * (nodeHeight + verticalGap) + 60, 180);
  const canvasWidth = numLevels * (nodeWidth + horizontalGap) + 80;
  Object.entries(levels).forEach(([level, levelNodes]) => {
    const levelNum = parseInt(level);
    const x = 40 + levelNum * (nodeWidth + horizontalGap);
    const totalHeight = levelNodes.length * nodeHeight + (levelNodes.length - 1) * verticalGap;
    const startY = (canvasHeight - totalHeight) / 2;
    levelNodes.forEach((node, index) => {
      nodePositions[node.id] = {
        x,
        y: startY + index * (nodeHeight + verticalGap)
      };
    });
  });
  const edgePaths = edges.map((edge, i) => {
    const from = nodePositions[edge.from];
    const to = nodePositions[edge.to];
    if (!from || !to) return null;
    const startX = from.x + nodeWidth;
    const startY = from.y + nodeHeight / 2;
    const endX = to.x - 6;
    const endY = to.y + nodeHeight / 2;
    const dx = endX - startX;
    const controlOffset = Math.min(dx * 0.4, 40);
    return {
      key: `edge-${i}`,
      path: `M ${startX} ${startY} C ${startX + controlOffset} ${startY}, ${endX - controlOffset} ${endY}, ${endX} ${endY}`
    };
  }).filter(Boolean);
  const uniqueId = `graph-${Math.random().toString(36).substring(2, 11)}`;
  const containerRef = useRef(null);
  const [scale, setScale] = useState(1);
  const [position, setPosition] = useState({
    x: 0,
    y: 0
  });
  const [isDragging, setIsDragging] = useState(false);
  const [dragStart, setDragStart] = useState({
    x: 0,
    y: 0
  });
  const handleWheel = e => {
    e.preventDefault();
    const delta = e.deltaY > 0 ? -0.1 : 0.1;
    setScale(s => Math.max(0.4, Math.min(2, s + delta)));
  };
  const handleMouseDown = e => {
    if (e.target.closest('button')) return;
    setIsDragging(true);
    setDragStart({
      x: e.clientX - position.x,
      y: e.clientY - position.y
    });
  };
  const handleMouseMove = e => {
    if (!isDragging) return;
    setPosition({
      x: e.clientX - dragStart.x,
      y: e.clientY - dragStart.y
    });
  };
  const handleMouseUp = () => setIsDragging(false);
  const handleFit = () => {
    const container = containerRef.current;
    if (container) {
      const containerWidth = container.clientWidth - 40;
      const containerHeight = container.clientHeight - 80;
      const scaleX = containerWidth / canvasWidth;
      const scaleY = containerHeight / canvasHeight;
      const fitScale = Math.min(scaleX, scaleY, 1);
      setScale(Math.max(0.4, fitScale));
    } else {
      setScale(1);
    }
    setPosition({
      x: 0,
      y: 0
    });
  };
  const handleZoomIn = () => setScale(s => Math.min(s + 0.15, 2));
  const handleZoomOut = () => setScale(s => Math.max(s - 0.15, 0.4));
  useEffect(() => {
    const container = containerRef.current;
    if (container) {
      container.addEventListener('wheel', handleWheel, {
        passive: false
      });
      return () => container.removeEventListener('wheel', handleWheel);
    }
  }, []);
  useEffect(() => {
    const timer = setTimeout(() => {
      const container = containerRef.current;
      if (container) {
        const containerWidth = container.clientWidth - 40;
        const containerHeight = container.clientHeight - 80;
        const scaleX = containerWidth / canvasWidth;
        const scaleY = containerHeight / canvasHeight;
        const fitScale = Math.min(scaleX, scaleY, 1);
        setScale(Math.max(0.4, fitScale));
      }
    }, 0);
    return () => clearTimeout(timer);
  }, [canvasWidth, canvasHeight]);
  const containerHeight = Math.max(300, Math.min(canvasHeight + 80, 420));
  return <div style={{
    marginTop: '16px',
    marginBottom: '24px'
  }}>
      {title && <div style={{
    fontSize: '13px',
    fontWeight: '500',
    color: '#64748b',
    marginBottom: '8px',
    fontFamily: 'system-ui, -apple-system, sans-serif'
  }}>
          {title}
        </div>}
      <div ref={containerRef} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} onMouseLeave={handleMouseUp} style={{
    position: 'relative',
    border: '1px solid #e5e7eb',
    borderRadius: '12px',
    backgroundColor: '#f9fafb',
    overflow: 'hidden',
    height: containerHeight,
    cursor: isDragging ? 'grabbing' : 'grab',
    userSelect: 'none'
  }}>
        {}
        <div style={{
    position: 'absolute',
    top: '12px',
    right: '12px',
    display: 'flex',
    gap: '6px',
    zIndex: 10
  }}>
          <button onClick={handleZoomIn} style={{
    width: '32px',
    height: '32px',
    border: '1px solid #d1d5db',
    borderRadius: '8px',
    backgroundColor: 'white',
    cursor: 'pointer',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: '18px',
    color: '#6b7280',
    fontWeight: '400',
    boxShadow: '0 1px 2px rgba(0,0,0,0.05)',
    transition: 'background-color 0.15s'
  }} onMouseOver={e => e.target.style.backgroundColor = '#f3f4f6'} onMouseOut={e => e.target.style.backgroundColor = 'white'}>+</button>
          <button onClick={handleZoomOut} style={{
    width: '32px',
    height: '32px',
    border: '1px solid #d1d5db',
    borderRadius: '8px',
    backgroundColor: 'white',
    cursor: 'pointer',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: '18px',
    color: '#6b7280',
    fontWeight: '400',
    boxShadow: '0 1px 2px rgba(0,0,0,0.05)',
    transition: 'background-color 0.15s'
  }} onMouseOver={e => e.target.style.backgroundColor = '#f3f4f6'} onMouseOut={e => e.target.style.backgroundColor = 'white'}>−</button>
          <button onClick={handleFit} style={{
    height: '32px',
    padding: '0 12px',
    border: '1px solid #d1d5db',
    borderRadius: '8px',
    backgroundColor: 'white',
    cursor: 'pointer',
    fontSize: '12px',
    fontWeight: '500',
    color: '#6b7280',
    fontFamily: 'system-ui, -apple-system, sans-serif',
    boxShadow: '0 1px 2px rgba(0,0,0,0.05)',
    transition: 'background-color 0.15s'
  }} onMouseOver={e => e.target.style.backgroundColor = '#f3f4f6'} onMouseOut={e => e.target.style.backgroundColor = 'white'}>Reset</button>
        </div>

        {}
        <div style={{
    transform: `translate(calc(-50% + ${position.x}px), calc(-50% + ${position.y}px)) scale(${scale})`,
    transformOrigin: 'center center',
    width: canvasWidth,
    height: canvasHeight,
    position: 'absolute',
    left: '50%',
    top: '50%'
  }}>
          {}
          <svg style={{
    position: 'absolute',
    top: 0,
    left: 0,
    width: canvasWidth,
    height: canvasHeight,
    pointerEvents: 'none',
    overflow: 'visible'
  }}>
            <defs>
              <marker id={`${uniqueId}-arrow`} markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto" markerUnits="strokeWidth">
                <path d="M0,1 L6,4 L0,7 L2,4 Z" fill="#9ca3af" />
              </marker>
            </defs>
            {edgePaths.map(edge => <path key={edge.key} d={edge.path} fill="none" stroke="#d1d5db" strokeWidth="2" strokeLinecap="round" markerEnd={`url(#${uniqueId}-arrow)`} />)}
          </svg>

          {}
          {nodes.map(node => {
    const pos = nodePositions[node.id];
    if (!pos) return null;
    const colors = nodeColors[node.type] || nodeColors.expression;
    const isHighlighted = highlighted === node.id;
    return <div key={node.id} style={{
      position: 'absolute',
      left: pos.x,
      top: pos.y,
      width: nodeWidth,
      height: nodeHeight,
      backgroundColor: isHighlighted ? '#fef2f2' : colors.bg,
      border: `1.5px solid ${isHighlighted ? '#f87171' : colors.border}`,
      borderRadius: '10px',
      display: 'flex',
      flexDirection: 'column',
      alignItems: 'center',
      justifyContent: 'center',
      boxShadow: isHighlighted ? '0 0 0 2px rgba(248, 113, 113, 0.25)' : '0 1px 2px rgba(0,0,0,0.04)',
      cursor: 'default'
    }}>
                <div style={{
      fontSize: '14px',
      marginBottom: '3px',
      color: isHighlighted ? '#ef4444' : colors.text,
      opacity: 0.85
    }}>
                  {colors.icon}
                </div>
                <div style={{
      fontSize: '11px',
      fontWeight: '500',
      color: isHighlighted ? '#dc2626' : colors.text,
      textAlign: 'center',
      padding: '0 8px',
      fontFamily: 'system-ui, -apple-system, sans-serif',
      lineHeight: '1.35',
      maxWidth: '100%'
    }}>
                  {node.label}
                </div>
              </div>;
  })}
        </div>

        {}
        <div style={{
    position: 'absolute',
    bottom: '12px',
    left: '12px',
    display: 'flex',
    gap: '14px',
    padding: '8px 12px',
    fontSize: '11px',
    color: '#6b7280',
    fontFamily: 'system-ui, -apple-system, sans-serif',
    backgroundColor: 'white',
    borderRadius: '8px',
    border: '1px solid #e5e7eb',
    boxShadow: '0 1px 2px rgba(0,0,0,0.05)'
  }}>
          {Object.entries(nodeColors).map(([type, colors]) => <div key={type} style={{
    display: 'flex',
    alignItems: 'center',
    gap: '5px'
  }}>
              <div style={{
    width: '16px',
    height: '16px',
    backgroundColor: colors.bg,
    border: `1px solid ${colors.border}`,
    borderRadius: '4px',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: '9px',
    color: colors.text
  }}>
                {colors.icon}
              </div>
              <span style={{
    textTransform: 'capitalize'
  }}>{type}</span>
            </div>)}
        </div>
      </div>
    </div>;
};

export const ExpressionViz = ({entries = []}) => {
  const allCells = entries.flatMap((entry, i) => [<div key={`key-${i}`} style={{
    backgroundColor: '#dbeafe',
    color: '#1e40af',
    padding: '12px 16px',
    borderRadius: '8px',
    fontFamily: 'ui-monospace, monospace',
    fontSize: '14px',
    fontWeight: '500'
  }}>
      {entry.key}
    </div>, <div key={`val-${i}`} style={{
    backgroundColor: '#ffffff',
    color: '#334155',
    padding: '12px 16px',
    borderRadius: '8px',
    fontFamily: 'ui-monospace, monospace',
    fontSize: '14px',
    border: '1px solid #e2e8f0'
  }}>
      {entry.value}
    </div>]);
  return <div style={{
    backgroundColor: '#f1f5f9',
    borderRadius: '12px',
    padding: '20px',
    margin: '16px 0'
  }}>
      <div style={{
    display: 'grid',
    gridTemplateColumns: 'auto 1fr',
    gap: '8px'
  }}>
        {allCells}
      </div>
    </div>;
};

export const DecisionTableViz = ({inputs = [], outputs = [], rows = []}) => {
  const colCount = inputs.length + outputs.length;
  const getFieldDisplay = field => !field || field === '-' ? '-' : field;
  const allCells = rows.flatMap((row, ri) => [...inputs.map((input, i) => <div key={`c-${ri}-in-${i}`} style={{
    backgroundColor: '#ffffff',
    padding: '12px 16px',
    borderRadius: '8px',
    fontFamily: 'ui-monospace, monospace',
    fontSize: '14px',
    border: '1px solid #e2e8f0'
  }}>
        {row[input.field] ?? ''}
      </div>), ...outputs.map((output, i) => <div key={`c-${ri}-out-${i}`} style={{
    backgroundColor: '#ffffff',
    padding: '12px 16px',
    borderRadius: '8px',
    fontFamily: 'ui-monospace, monospace',
    fontSize: '14px',
    border: '1px solid #e2e8f0'
  }}>
        {row[output.field] ?? ''}
      </div>)]);
  return <div style={{
    backgroundColor: '#f1f5f9',
    borderRadius: '12px',
    padding: '20px',
    overflowX: 'auto',
    margin: '16px 0'
  }}>
      <div style={{
    display: 'grid',
    gridTemplateColumns: `repeat(${colCount}, 1fr)`,
    gap: '8px'
  }}>
        {}
        <div key="group-inputs" style={{
    gridColumn: `span ${inputs.length}`,
    padding: '8px 16px',
    fontWeight: '600',
    fontSize: '13px',
    color: '#374151',
    borderBottom: '2px solid #e2e8f0'
  }}>
          Inputs
        </div>
        <div key="group-outputs" style={{
    gridColumn: `span ${outputs.length}`,
    padding: '8px 16px',
    fontWeight: '600',
    fontSize: '13px',
    color: '#374151',
    borderBottom: '2px solid #e2e8f0'
  }}>
          Outputs
        </div>

        {}
        {inputs.map((input, i) => <div key={`h-in-${i}`} style={{
    backgroundColor: '#f0abfc',
    color: '#581c87',
    padding: '12px 16px',
    borderRadius: '8px',
    fontWeight: '500'
  }}>
            <div>{input.name}</div>
            <div style={{
    display: 'inline-block',
    marginTop: '6px',
    padding: '2px 8px',
    fontSize: '12px',
    fontWeight: '400',
    backgroundColor: 'rgba(88, 28, 135, 0.15)',
    borderRadius: '4px',
    fontFamily: 'ui-monospace, monospace'
  }}>
              {getFieldDisplay(input.field)}
            </div>
          </div>)}
        {outputs.map((output, i) => <div key={`h-out-${i}`} style={{
    backgroundColor: '#a5b4fc',
    color: '#312e81',
    padding: '12px 16px',
    borderRadius: '8px',
    fontWeight: '500'
  }}>
            <div>{output.name}</div>
            <div style={{
    display: 'inline-block',
    marginTop: '6px',
    padding: '2px 8px',
    fontSize: '12px',
    fontWeight: '400',
    backgroundColor: 'rgba(49, 46, 129, 0.15)',
    borderRadius: '4px',
    fontFamily: 'ui-monospace, monospace'
  }}>
              {getFieldDisplay(output.field)}
            </div>
          </div>)}

        {}
        {allCells}
      </div>
    </div>;
};

This guide shows how to build insurance underwriting rules that assess risk, calculate premiums, and determine policy terms.

## What you'll build

An underwriting decision that:

* Evaluates applicant risk factors
* Calculates risk-adjusted premiums
* Determines coverage eligibility
* Applies regulatory constraints

<Note>
  Want to skip ahead? <a href="/downloads/insurance.json" download>Download the completed decision</a> and import it directly.
</Note>

### Decision flow

<DecisionGraphViz
  title="Auto Insurance Underwriting"
  nodes={[
{ id: "input", type: "input", label: "Application Data" },
{ id: "driver", type: "table", label: "Driver Risk" },
{ id: "history", type: "table", label: "Driving History" },
{ id: "vehicle", type: "table", label: "Vehicle Risk" },
{ id: "basePremium", type: "expression", label: "Base Premium" },
{ id: "coverage", type: "table", label: "Coverage Adj" },
{ id: "credit", type: "table", label: "Credit Adj" },
{ id: "final", type: "expression", label: "Final Premium" },
{ id: "eligibility", type: "table", label: "Eligibility" }
]}
  edges={[
{ from: "input", to: "driver" },
{ from: "input", to: "history" },
{ from: "input", to: "vehicle" },
{ from: "driver", to: "basePremium" },
{ from: "history", to: "basePremium" },
{ from: "vehicle", to: "basePremium" },
{ from: "basePremium", to: "coverage" },
{ from: "coverage", to: "credit" },
{ from: "credit", to: "final" },
{ from: "final", to: "eligibility" }
]}
/>

## Example: Auto insurance quote

### Input data

```json theme={null}
{
  "applicant": {
    "age": 28,
    "yearsLicensed": 10,
    "accidentsLast5Years": 0,
    "violationsLast3Years": 1,
    "creditTier": "good"
  },
  "vehicle": {
    "year": 2022,
    "make": "Toyota",
    "model": "Camry",
    "value": 28000,
    "safetyRating": 5,
    "antiTheft": true
  },
  "coverage": {
    "type": "full",
    "deductible": 500,
    "state": "CA"
  }
}
```

## Step 1: Driver risk assessment

Score driver risk factors:

<DecisionTableViz
  inputs={[
{ name: "Age", field: "applicant.age" },
{ name: "Years Licensed", field: "applicant.yearsLicensed" }
]}
  outputs={[
{ name: "Age Factor", field: "ageFactor" },
{ name: "Experience Factor", field: "expFactor" }
]}
  rows={[
{ "applicant.age": "< 21", "applicant.yearsLicensed": "", ageFactor: "1.8", expFactor: "" },
{ "applicant.age": "[21..25]", "applicant.yearsLicensed": "", ageFactor: "1.4", expFactor: "" },
{ "applicant.age": "[26..65]", "applicant.yearsLicensed": "", ageFactor: "1.0", expFactor: "" },
{ "applicant.age": "> 65", "applicant.yearsLicensed": "", ageFactor: "1.2", expFactor: "" },
{ "applicant.age": "", "applicant.yearsLicensed": "< 2", ageFactor: "", expFactor: "1.5" },
{ "applicant.age": "", "applicant.yearsLicensed": "[2..5]", ageFactor: "", expFactor: "1.2" },
{ "applicant.age": "", "applicant.yearsLicensed": "> 5", ageFactor: "", expFactor: "1.0" }
]}
/>

## Step 2: Driving history

Assess accidents and violations:

<DecisionTableViz
  inputs={[
{ name: "Accidents (5yr)", field: "applicant.accidentsLast5Years" },
{ name: "Violations (3yr)", field: "applicant.violationsLast3Years" }
]}
  outputs={[
{ name: "History Factor", field: "historyFactor" },
{ name: "Surcharge", field: "surcharge" }
]}
  rows={[
{ "applicant.accidentsLast5Years": "0", "applicant.violationsLast3Years": "0", historyFactor: "0.9", surcharge: "0" },
{ "applicant.accidentsLast5Years": "0", "applicant.violationsLast3Years": "1", historyFactor: "1.0", surcharge: "0" },
{ "applicant.accidentsLast5Years": "0", "applicant.violationsLast3Years": "[2..3]", historyFactor: "1.2", surcharge: "50" },
{ "applicant.accidentsLast5Years": "1", "applicant.violationsLast3Years": "[0..1]", historyFactor: "1.3", surcharge: "100" },
{ "applicant.accidentsLast5Years": "1", "applicant.violationsLast3Years": ">= 2", historyFactor: "1.5", surcharge: "200" },
{ "applicant.accidentsLast5Years": "2", "applicant.violationsLast3Years": "", historyFactor: "1.8", surcharge: "350" },
{ "applicant.accidentsLast5Years": ">= 3", "applicant.violationsLast3Years": "", historyFactor: "2.5", surcharge: "500" }
]}
/>

## Step 3: Vehicle risk

Assess vehicle factors:

<DecisionTableViz
  inputs={[
{ name: "Vehicle Age", field: "vehicleAge" },
{ name: "Safety Rating", field: "vehicle.safetyRating" },
{ name: "Anti-Theft", field: "vehicle.antiTheft" }
]}
  outputs={[
{ name: "Vehicle Factor", field: "vehicleFactor" }
]}
  rows={[
{ vehicleAge: "< 3", "vehicle.safetyRating": ">= 4", "vehicle.antiTheft": "true", vehicleFactor: "0.9" },
{ vehicleAge: "< 3", "vehicle.safetyRating": ">= 4", "vehicle.antiTheft": "false", vehicleFactor: "1.0" },
{ vehicleAge: "[3..10]", "vehicle.safetyRating": ">= 3", "vehicle.antiTheft": "", vehicleFactor: "1.0" },
{ vehicleAge: "[3..10]", "vehicle.safetyRating": "< 3", "vehicle.antiTheft": "", vehicleFactor: "1.1" },
{ vehicleAge: "> 10", "vehicle.safetyRating": "", "vehicle.antiTheft": "", vehicleFactor: "1.15" }
]}
/>

## Step 4: Calculate base premium

<ExpressionViz
  entries={[
{ key: "vehicleAge", value: "d().year() - vehicle.year" },
{ key: "baseRate", value: "vehicle.value * 0.03" },
{ key: "driverRisk", value: "DriverRisk.ageFactor * DriverRisk.expFactor" },
{ key: "historyRisk", value: "DrivingHistory.historyFactor" },
{ key: "vehicleRisk", value: "VehicleRisk.vehicleFactor" },
{ key: "combinedFactor", value: "driverRisk * historyRisk * vehicleRisk" },
{ key: "adjustedPremium", value: "baseRate * combinedFactor + DrivingHistory.surcharge" }
]}
/>

## Step 5: Coverage and deductible adjustments

<DecisionTableViz
  inputs={[
{ name: "Coverage Type", field: "coverage.type" },
{ name: "Deductible", field: "coverage.deductible" }
]}
  outputs={[
{ name: "Coverage Multiplier", field: "coverageMult" },
{ name: "Deductible Discount", field: "deductDiscount" }
]}
  rows={[
{ "coverage.type": '"full"', "coverage.deductible": "", coverageMult: "1.4", deductDiscount: "" },
{ "coverage.type": '"standard"', "coverage.deductible": "", coverageMult: "1.0", deductDiscount: "" },
{ "coverage.type": '"liability_only"', "coverage.deductible": "", coverageMult: "0.6", deductDiscount: "" },
{ "coverage.type": "", "coverage.deductible": ">= 1000", coverageMult: "", deductDiscount: "0.15" },
{ "coverage.type": "", "coverage.deductible": "[500..999]", coverageMult: "", deductDiscount: "0.10" },
{ "coverage.type": "", "coverage.deductible": "< 500", coverageMult: "", deductDiscount: "0" }
]}
/>

## Step 6: Credit-based adjustment

<DecisionTableViz
  inputs={[
{ name: "Credit Tier", field: "applicant.creditTier" }
]}
  outputs={[
{ name: "Credit Factor", field: "creditFactor" }
]}
  rows={[
{ "applicant.creditTier": '"excellent"', creditFactor: "0.85" },
{ "applicant.creditTier": '"good"', creditFactor: "1.0" },
{ "applicant.creditTier": '"fair"', creditFactor: "1.15" },
{ "applicant.creditTier": '"poor"', creditFactor: "1.35" }
]}
/>

## Step 7: Final premium calculation

<ExpressionViz
  entries={[
{ key: "premiumBeforeCredit", value: "adjustedPremium * CoverageAdj.coverageMult * (1 - CoverageAdj.deductDiscount)" },
{ key: "annualPremium", value: "round(premiumBeforeCredit * CreditAdj.creditFactor, 2)" },
{ key: "monthlyPremium", value: "round(annualPremium / 12, 2)" }
]}
/>

## Step 8: Eligibility decision

<DecisionTableViz
  inputs={[
{ name: "Accidents", field: "accidents" },
{ name: "Violations", field: "violations" },
{ name: "Combined Factor", field: "factor" }
]}
  outputs={[
{ name: "Decision", field: "decision" },
{ name: "Reason", field: "reason" }
]}
  rows={[
{ accidents: ">= 4", violations: "", factor: "", decision: '"decline"', reason: '"Excessive accidents"' },
{ accidents: "", violations: ">= 5", factor: "", decision: '"decline"', reason: '"Excessive violations"' },
{ accidents: "", violations: "", factor: "> 3.0", decision: '"refer"', reason: '"High risk - manual review"' },
{ accidents: "", violations: "", factor: "", decision: '"approve"', reason: '"Meets guidelines"' }
]}
/>

## Output structure

```json theme={null}
{
  "decision": "approve",
  "annualPremium": 1247.50,
  "monthlyPremium": 103.96,
  "riskFactors": {
    "driver": 1.0,
    "history": 1.0,
    "vehicle": 0.9,
    "credit": 1.0,
    "combined": 0.9
  },
  "coverage": {
    "type": "full",
    "deductible": 500,
    "liability": 100000
  }
}
```

## Regulatory compliance

### State-specific rules

<DecisionTableViz
  inputs={[
{ name: "State", field: "coverage.state" }
]}
  outputs={[
{ name: "Min Liability", field: "minLiability" },
{ name: "Credit Allowed", field: "creditAllowed" }
]}
  rows={[
{ "coverage.state": '"CA"', minLiability: "15000", creditAllowed: "false" },
{ "coverage.state": '"TX"', minLiability: "30000", creditAllowed: "true" },
{ "coverage.state": '"NY"', minLiability: "25000", creditAllowed: "true" },
{ "coverage.state": "", minLiability: "25000", creditAllowed: "true" }
]}
/>

## Best practices

**Audit all decisions** — Log every factor for regulatory review.

**Version rating tables** — Track when rates change for policy renewals.

**Handle edge cases** — New drivers, antique vehicles, rideshare use.

**Test extensively** — Validate against known good quotes.
