# Underwriting Plugin



The Underwriting Plugin allows you to automatically add or remove [underwriting](/features/underwriting) flags to or from quotes or policy transactions.

The following API endpoints trigger the Underwriting Plugin:

* <ApiLink name="underwriteQuote">
    Underwrite a Quote
  </ApiLink>
* <ApiLink name="acceptQuote">
    Accept a Quote
  </ApiLink>
* <ApiLink name="issueQuote">
    Issue a Quote
  </ApiLink>
* <ApiLink name="underwriteTransaction">
    Underwrite a Policy Transaction
  </ApiLink>
* <ApiLink name="acceptTransaction">
    Accept a Policy Transaction
  </ApiLink>
* <ApiLink name="issueTransaction">
    Issue a Policy Transaction
  </ApiLink>

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](/features/policy-management/policy-transactions#Process).

Underwriting Flags [#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](/features/policy-management/policy-transactions#Process), 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.

<Callout type="warn">
  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.
</Callout>

Implementation [#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:

```java
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 level
* `elementLocator` - An optional element locator associated with the flag
* `tag` - An optional unique identifier associated with the flag
* `note` - An optional note associated with the flag

Checking for Existing Flags [#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 [#example]

The following example is based on the Prism configuration. Contact your Socotra representative for more information.

```java
// 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 [#best-practices]

1. **Always Check Existing Flags First**
   * Use `DataFetcher.getInstance().getQuoteUnderwritingFlags()`

   * Check existing `tag` values to avoid unintentionally adding previously cleared flags

   * Make informed decisions about flag creation and updates

2. **Handle All Request Types**
   * Implement both quote and policy transaction methods

   * Check for segment presence in policy transaction requests

3. **Use Unique Rule Identifiers**
   * Set meaningful `tag` values for each flag type

   * Include element-specific identifiers when needed

4. **Provide Clear Flag Messages**
   * Write actionable notes that help underwriters understand the issue

   * Include relevant context and next steps

5. **Test Thoroughly**
   * Verify flag creation and duplicate prevention

   * Test both quote and policy transaction scenarios

   * Validate flag levels and their impact on policy progression

Next Steps [#next-steps]

* [Document Data Snapshot Plugin](/configuration/plugins/document-data-snapshot)

See Also [#see-also]

* [Plugins Overview](/configuration/plugins/overview)
* [Underwriting](/features/underwriting)
* [Policy Lifecycle](/features/policy-management/policy-transactions#Process)
* [Quotes API](/api/quotes/quotes)
* [Policy Transactions API](/api/policy-management/policy-transactions)
* [Plugin Data Fetcher](/configuration/plugins/overview#PluginDataFetcher)
