Underwriting Plugin
The Underwriting Plugin allows you to automatically add or remove underwriting flags to or from quotes or policy transactions.
The following API endpoints trigger the Underwriting Plugin:
If the Underwriting Plugin is triggered, but the plugin has not been implemented, the system will automatically allow quotes and policy transactions to proceed to the accepted stage of the policy lifecycle.
Underwriting Flags
The Underwriting Plugin can add or remove the following underwriting flags to or from quotes or policy transactions:
approve- The system will approve the quote or policy transaction and allow it to proceed to the next stage of the policy lifecycle, regardless of any other flags that have been added to it during the current execution of the Underwriting Plugin. Quotes and policy transactions cannot be approved if they have already been rejected as a result of a previous execution of the Underwriting Plugin.block- The system will block the quote or policy transaction from proceeding to the next stage of the policy lifecycle.decline- The system will block the quote or policy transaction from proceeding to the next stage of the policy lifecycle.reject- The system will permanently block the quote or policy transaction from proceeding to the next stage of the policy lifecycle.info- This flag can be used to provide underwriting information, but has no effect.
The block and decline flags both prevent quotes and policy transactions from proceeding to the next stage of the policy lifecycle until the flags have been removed or an approve flag is added. The block and decline flags function the same way, providing underwriters more flexibility to signal different types of denials.
If no flags have been added to a quote or policy transaction, the system will allow it to proceed to the accepted stage of the policy lifecycle.
Important
The reject flag permanently blocks a quote or policy transaction from proceeding to the next stage of the policy lifecycle. If this is not acceptable, we recommend using the decline or block flags as alternatives.
Implementation
Create a new Java class in the src/main/java/com/socotra/deployment/customer folder. All plugin code must be contained within this folder. We named our class UnderwritingPluginImpl.java in the example below, but you can name your class whatever you’d like.
Implement the UnderwritingPlugin interface, and override the method corresponding to your target entity type.
For example, the following class contains a method that approves commercial auto quotes:
public class UnderwritingPluginImpl implements UnderwritingPlugin {
private static final Logger log = LoggerFactory.getLogger(UnderwritingPluginImpl.class);
// Approves commercial auto quotes
@Override
public UnderwritingModification underwrite(CommercialAutoQuoteRequest commercialAutoQuoteRequest) {
return UnderwritingModification.builder()
.flagsToCreate(List.of(UnderwritingFlagCore.builder()
.level(UnderwritingLevel.approve)
.elementLocator(Optional.of(commercialAutoQuoteRequest.quote().locator()))
.tag(Optional.of("EXAMPLE_UNIQUE_IDENTIFIER"))
.note(Optional.of("This is an underwriting note."))
.build()))
.build();
}
}
The method argument contains the quote or policy to be underwritten. In the example above, the argument contains a commercial auto quote. Change the method argument class to CommercialAutoRequest to underwrite commercial auto policy transactions instead.
The method returns an UnderwritingModification object, which contains a list of UnderwritingFlagCore objects that will be added to the quote or policy transaction. Each UnderwritingFlagCore object contains the following fields:
level- The flag levelelementLocator- An optional element locator associated with the flagtag- An optional unique identifier associated with the flagnote- An optional note associated with the flag
Checking for Existing Flags
Implementations must check for existing flags before adding new flags to avoid unintentionally adding previously cleared flags. The example below demonstrates the recommended approach to checking flags.
Example
The following example is based on the Prism configuration. Contact your Socotra representative for more information.
// Blocks commercial auto quotes and policy transactions based on driver experience and vehicle value
public class UnderwritingPluginImpl implements UnderwritingPlugin {
private static final Logger log = LoggerFactory.getLogger(UnderwritingPluginImpl.class);
@Override
public UnderwritingModification underwrite(CommercialAutoQuoteRequest commercialAutoQuoteRequest) {
return underwriteCommercialAuto(commercialAutoQuoteRequest.quote());
}
@Override
public UnderwritingModification underwrite(CommercialAutoRequest commercialAutoRequest) {
if (commercialAutoRequest.segment().isPresent()) {
return underwriteCommercialAuto(commercialAutoRequest.segment().get());
}
return UnderwritingModification.builder().build();
}
private UnderwritingModification underwriteCommercialAuto(CommercialAuto commercialAuto) {
// Check existing flags to avoid adding previously cleared flags
DataFetcher dataFetcher = DataFetcher.getInstance();
UnderwritingFlags flags = dataFetcher.getQuoteUnderwritingFlags(commercialAuto.locator());
Set<String> existingRuleIds = new HashSet<>();
existingRuleIds.addAll(flags.flags().stream()
.map(flag -> flag.tag().orElse(""))
.filter(tag -> !tag.isEmpty())
.collect(Collectors.toSet()));
existingRuleIds.addAll(flags.clearedFlags().stream()
.map(flag -> flag.tag().orElse(""))
.filter(tag -> !tag.isEmpty())
.collect(Collectors.toSet()));
List<UnderwritingFlagCore> newFlags = new ArrayList<>();
// Evaluate driver experience
for (Driver driver : commercialAuto.driverSchedule().drivers()) {
if (driver.data().yearsOfExperience() < 5
&& !existingRuleIds.contains("INEXPERIENCED_DRIVER" + driver.locator())) {
newFlags.add(
createFlag(
UnderwritingLevel.block,
driver.locator(),
"INEXPERIENCED_DRIVER_" + driver.charges(),
"Driver has less than 5 years of experience."
)
);
}
}
// Evaluate vehicle value
for (Vehicle vehicle : commercialAuto.vehicleSchedule().vehicles()) {
if (vehicle.data().currentValue() != null
&& vehicle.data().currentValue().compareTo(new BigDecimal("100000")) > 0
&& !existingRuleIds.contains("HIGH_VALUE_VEHICLE")) {
newFlags.add(
createFlag(
UnderwritingLevel.block,
vehicle.locator(),
"HIGH_VALUE_VEHICLE_" + vehicle.locator().toString(),
"Vehicle value exceeds underwriting guidelines."
)
);
}
}
// Add flags
return UnderwritingModification.builder()
.flagsToCreate(newFlags)
.build();
}
private UnderwritingFlagCore createFlag(UnderwritingLevel level, ULID locator, String tag, String note) {
return UnderwritingFlagCore.builder()
.level(level)
.elementLocator(Optional.of(locator))
.tag(Optional.of(tag))
.note(Optional.of(note))
.build();
}
}
Best Practices
Always Check Existing Flags First
Use
DataFetcher.getInstance().getQuoteUnderwritingFlags()Check existing
tagvalues to avoid unintentionally adding previously cleared flagsMake informed decisions about flag creation and updates
Handle All Request Types
Implement both quote and policy transaction methods
Check for segment presence in policy transaction requests
Use Unique Rule Identifiers
Set meaningful
tagvalues for each flag typeInclude element-specific identifiers when needed
Provide Clear Flag Messages
Write actionable notes that help underwriters understand the issue
Include relevant context and next steps
Test Thoroughly
Verify flag creation and duplicate prevention
Test both quote and policy transaction scenarios
Validate flag levels and their impact on policy progression