Skip to content

Underwriting Workflow

Implementing automated underwriting rules and decision-making.


Complete Underwriting Function

fn UnderwriteQuote(customer, items, premium) {
    // 1. Age validation
    if (customer.Age < 18) {
        $Decline("Applicant must be 18 or older");
        return "Declined";
    }

    if (customer.Age > 80) {
        $Level2Referral("Applicant age over 80 - requires senior review");
        return "Referred";
    }

    // 2. Claim history
    if (customer.ClaimCount > 5) {
        $Decline("Excessive claim history - unacceptable risk");
        return "Declined";
    }

    if (customer.ClaimCount >= 3) {
        $Level1Referral(customer.ClaimCount + " previous claims - requires assessment");
        return "Referred";
    }

    // 3. High value items
    let highValueItems = $Filter(items, fn(item) => item.Value > 5000);
    if ($Count(highValueItems) > 0) {
        $Level2Referral("High value items require valuation");
        for (let item in highValueItems) {
            $AddEndorsement("VAL" + item.Id, "Proof of value required for " + item.Name);
        }
        return "Referred";
    }

    // 4. Premium thresholds
    if (premium > 25000) {
        $Level3Referral("Premium exceeds £25,000 - senior approval required");
        return "Referred";
    }

    if (premium > 10000) {
        $Level2Referral("Premium exceeds £10,000 - manager approval required");
        return "Referred";
    }

    // 5. Security requirements
    if (customer.SecurityLevel < 3) {
        $AddEndorsement("SEC01", "BS3621 locks required on all external doors");
        $AddEndorsement("SEC02", "Burglar alarm required - certificate of installation needed");
    }

    // 6. Professional use
    if (customer.ProfessionalUse) {
        $Level1Referral("Professional use declared - requires review");
        $AddEndorsement("EX01", "Business use excluded unless specifically agreed");
    }

    // 7. Territory risk
    if (customer.TerritoryRisk == "HIGH") {
        $Level1Referral("High risk postcode - enhanced security required");
        $AddEndorsement("SEC03", "24-hour monitored alarm system required");
    }

    // Accept if no issues
    $AddUnderwriterDecision("Accept", "Standard risk - auto-accepted", "NoAuthority");
    return "Accepted";
}

Integration with Quote Process

// Complete quote with underwriting
fn ProcessCompleteQuote() {
    // 1. Calculate premium
    $SetCurrencyToPricingMatrix("GBP");
    $SetCommissionRateToPricingMatrix(0.15);
    $SetIptRateToPricingMatrix(0.12);

    let age = $CalculateAge($Today(), Customer.DateOfBirth);
    let basePremium = 450.00;

    if (age < 25) { basePremium = 600.00; }
    else if (age > 70) { basePremium = 550.00; }

    $AddItemToPricingMatrix("Contents Cover", basePremium, 1.0);

    if (Customer.NoClaimsYears >= 3) {
        $AddFactorToPricingMatrix("No Claims Discount", 0.90, true, 0);
    }

    $SetFeeToPricingMatrix(25.00);
    $CalculatePremiumAdditive("NewBusiness");

    // 2. Run underwriting
    let uwDecision = UnderwriteQuote(Customer, Items, PricingMatrix.Premium);

    // 3. Build response based on decision
    let response = {
        Premium: PricingMatrix.Premium,
        MonthlyPayment: Output.Monthly.MonthlyPayment,
        Decision: uwDecision,
        Endorsements: Endorsements
    };

    // Check for decline
    let declined = $FindEquals(UnderwriterDecisions, "Decision", "Decline");
    if (declined != null) {
        response.DeclineReason = declined.Message;
        return response;
    }

    // Check for referrals
    let referrals = $Filter(UnderwriterDecisions, fn(d) => $Contains(d.Decision, "Referral"));
    if ($Count(referrals) > 0) {
        response.ReferralReasons = $Map(referrals, fn(r) => r.Message);
        response.RequiresManualReview = true;
    }

    return response;
}

let quoteResult = ProcessCompleteQuote();

if (quoteResult.Decision == "Declined") {
    $Log("Quote declined: " + quoteResult.DeclineReason);
} else if (quoteResult.Decision == "Referred") {
    $Log("Quote referred for review");
    for (let reason in quoteResult.ReferralReasons) {
        $Log("- " + reason);
    }
} else {
    $Log("Quote accepted - Premium: £" + $ToString(quoteResult.Premium));
}

Risk-Based Underwriting

fn CalculateRiskScore(customer, items) {
    let riskScore = 100;  // Start at baseline

    // Age risk
    if (customer.Age < 25) { riskScore = riskScore + 30; }
    else if (customer.Age > 75) { riskScore = riskScore + 20; }

    // Claim history risk
    riskScore = riskScore + (customer.ClaimCount * 15);

    // High value items risk
    let highValueCount = $Count($Filter(items, fn(i) => i.Value > 3000));
    riskScore = riskScore + (highValueCount * 10);

    // Security risk
    if (customer.SecurityLevel < 3) { riskScore = riskScore + 25; }

    // Territory risk
    if (customer.TerritoryRisk == "HIGH") { riskScore = riskScore + 40; }
    else if (customer.TerritoryRisk == "MEDIUM") { riskScore = riskScore + 15; }

    // No claims bonus
    if (customer.NoClaimsYears > 5) { riskScore = riskScore - 20; }

    return riskScore;
}

fn UnderwriteByRiskScore() {
    let riskScore = CalculateRiskScore(Customer, Items);

    $Log("Risk Score: " + $ToString(riskScore));

    if (riskScore > 200) {
        $Decline("Risk score " + $ToString(riskScore) + " exceeds acceptable threshold");
    } else if (riskScore > 150) {
        $Level2Referral("High risk score: " + $ToString(riskScore) + " - requires assessment");
    } else if (riskScore > 120) {
        $Level1Referral("Elevated risk score: " + $ToString(riskScore));
    } else {
        $AddUnderwriterDecision("Accept", "Risk score within acceptable range: " + $ToString(riskScore), "NoAuthority");
    }
}

Territory-Based Rules

fn UnderwriteTerritory(postcode) {
    let territoryRisk = DetermineT erritoryRisk(postcode);

    if (territoryRisk.Level == "UNACCEPTABLE") {
        $Decline("Territory risk unacceptable - postcode " + postcode);
        return;
    }

    if (territoryRisk.Level == "HIGH") {
        // High risk area requirements
        if (Customer.SecurityLevel < 5) {
            $Decline("Insufficient security for high-risk territory");
            return;
        }

        $AddEndorsement("TER01", "Property located in high-risk area");
        $AddEndorsement("SEC05", "24-hour monitored alarm system mandatory");
        $Level2Referral("High risk territory - enhanced security confirmed");

    } else if (territoryRisk.Level == "MEDIUM") {
        // Medium risk area
        if (Customer.SecurityLevel < 3) {
            $Level1Referral("Security below standard for medium-risk area");
        }

        $AddEndorsement("SEC01", "BS3621 locks required on all external doors");
    }

    // Low risk areas - no additional requirements
}

Claim History Analysis

fn AnalyzeClaimHistory(claims) {
    if ($Count(claims) == 0) {
        $Log("No claim history - positive indicator");
        return { Accept: true, Factor: 0.90 };  // 10% discount
    }

    // Recent claims (last 3 years)
    let recentClaims = $Filter(claims, fn(c) =>
        $DifferenceInDays($Today(), c.Date) <= 1095  // 3 years
    );

    if ($Count(recentClaims) > 2) {
        $Level1Referral($ToString($Count(recentClaims)) + " claims in last 3 years");
        return { Accept: true, Factor: 1.30 };  // 30% loading
    }

    // High value claims
    let highValueClaims = $Filter(claims, fn(c) => c.Amount > 5000);
    if ($Count(highValueClaims) > 0) {
        $Level1Referral("Previous high value claim(s) - £" + $ToString($Sum($Map(highValueClaims, fn(c) => c.Amount))));
    }

    // Fraud indicators
    let fraudIndicators = $Filter(claims, fn(c) => c.FraudFlag == true);
    if ($Count(fraudIndicators) > 0) {
        $Decline("Previous fraud indicators on claim history");
        return { Accept: false };
    }

    return { Accept: true, Factor: 1.05 };  // Small loading for claims
}

Endorsement Management

fn AddStandardEndorsements(customer, items) {
    // Alarm requirement for high values
    let totalValue = $Sum($Map(items, fn(i) => i.Value));
    if (totalValue > 30000) {
        $AddEndorsement("SEC04", "Approved burglar alarm required - NSI/SSAIB Gold standard");
    }

    // Single item limits
    let maxSingleItem = $Max($Map(items, fn(i) => i.Value));
    if (maxSingleItem > 10000) {
        $AddEndorsement("LIM01", "Single item limit: £" + $ToString(maxSingleItem) + " - valuation required");
    }

    // Professional use exclusion
    $AddEndorsement("EX02", "Professional, business, or trade use excluded");

    // Standard exclusions
    $AddEndorsement("EX03", "War, terrorism, and nuclear risks excluded");
    $AddEndorsement("EX04", "Unoccupied property exclusion - notify if property unoccupied >30 days");
}

Complete Example

// Full underwriting workflow
fn ProcessQuoteWithUnderwriting() {
    // Customer data
    let Customer = {
        Age: 42,
        DateOfBirth: $ToDate("1981-08-15"),
        ClaimCount: 1,
        NoClaimsYears: 4,
        SecurityLevel: 4,
        TerritoryRisk: "MEDIUM",
        ProfessionalUse: false,
        PostCode: "SW1A 1AA"
    };

    let Items = [
        { Id: 1, Name: "Jewelry", Value: 8000 },
        { Id: 2, Name: "Art", Value: 12000 },
        { Id: 3, Name: "Electronics", Value: 5000 }
    ];

    // Calculate premium
    $SetCurrencyToPricingMatrix("GBP");
    $SetCommissionRateToPricingMatrix(0.15);
    $SetIptRateToPricingMatrix(0.12);

    let basePremium = 650.00;  // Based on total value
    $AddItemToPricingMatrix("High Value Contents", basePremium, 1.0);

    if (Customer.NoClaimsYears >= 3) {
        $AddFactorToPricingMatrix("No Claims Discount", 0.90, true, 0);
    }

    $SetFeeToPricingMatrix(25.00);
    $CalculatePremiumAdditive("NewBusiness");

    // Underwriting
    let uwDecision = UnderwriteQuote(Customer, Items, PricingMatrix.Premium);

    // Add standard endorsements
    AddStandardEndorsements(Customer, Items);

    // Territory-specific rules
    UnderwriteTerritory(Customer.PostCode);

    // Build final response
    return {
        Decision: uwDecision,
        Premium: PricingMatrix.Premium,
        Monthly: Output.Monthly.MonthlyPayment,
        Endorsements: Endorsements,
        UnderwriterDecisions: UnderwriterDecisions
    };
}

let result = ProcessQuoteWithUnderwriting();
$Log("Final Decision: " + result.Decision);

Next Steps