# Plugins Overview



Plugins are software components within the Socotra Insurance Suite that allow you to implement custom business logic written in Java and modify system processes such as underwriting, rating, and payment execution.

Once plugin [implementations](/configuration/plugins/overview#implementation) have been [deployed](/configuration/general-topics/deployment), the system will automatically execute plugin code when entities move to a specific stage of their lifecycles, in response to system events, or when calling certain API endpoints. See the feature guide for each plugin for more information.

The following plugins are available in the Socotra Insurance Suite:

* [Precommit](/configuration/plugins/precommit) - Modify an entity before saving it in the system
* [Validation](/configuration/plugins/validation) - Check if an entity meets a set of requirements
* [Rating](/configuration/plugins/rating) - Calculate charges for quotes and policy transactions
* [Underwriting](/configuration/plugins/underwriting) - Assess risk and determine approval status
* [Document Selection](/configuration/plugins/document-selection) - Determine which [documents](/features/documents/document-management) should be attached to an entity
* [Document Data Snapshot](/configuration/plugins/document-data-snapshot) - Add data and metadata to [documents](/features/documents/document-management)
* [Installments](/configuration/plugins/installments) - Configure [installments](/features/billing/installments-and-installment-lattices)
* [Autopay](/configuration/plugins/autopay) - Configure [automatic payments](/features/billing/autopay)
* [Payment Post-Processing Plugin](/configuration/plugins/payment-post-processing) - Configure payment execution post-processing logic
* [Cancellation](/configuration/plugins/cancellation) - Configure retention charges when policies are cancelled
* [Automation](/configuration/plugins/automation) - Implement custom business logic using the Socotra API and third-party APIs
* [Integrations](/configuration/plugins/integrations) - Customize email delivery logic used by the [Socotra Assistant](/ai-guide/assistant/overview)

Implementation [#implementation]

Plugins are implemented as Java classes.

Implementations can be classified as either `Global` or `Product` implementations. `Global` implementations are located in the top-level `configuration-name/plugins/java` folder within the configuration folder structure, and `Product` implementations are located in product folders using the following folder structure: `configuration-name/productName/plugins/java`.

If both a `Global` and a `Product` implementation of a plugin are currently deployed, the system will only execute the `Product` implementation.

<Callout>
  [Automation Plugin](/configuration/plugins/automation) implementations must be `Global`.
</Callout>

Once an implementation has been deployed, `Global` implementations will be moved to the `src/main/java/com/socotra/deployment/customer` folder, and `Product` implementations will be moved to a subfolder using the following folder structure: `src/main/java/com/socotra/deployment/customer/productname`. Implementations can be created and modified directly within these folder structures instead of the configuration folder structures.

To implement a plugin, create a Java class in one of the above folders, implement the interface for the plugin, and override the method specific to your target entity type.

For example, the following class implements the [Validation Plugin](/configuration/plugins/validation) and validates commercial accounts, commercial auto quotes, and commercial policy transactions:

```java
// Validates commercial accounts, commercial auto quotes, and commercial policy transactions

public class ValidationPluginImpl implements ValidationPlugin {
    private static final Logger log = LoggerFactory.getLogger(ValidationPluginImpl.class);

    @Override
    public ValidationItem validate(CommercialAccountRequest commercialAccountRequest) {
        return ValidationItem.builder().build();
    }

    @Override
    public ValidationItem validate(CommercialAutoQuoteRequest commercialAutoQuoteRequest) {
        return ValidationItem.builder().build();
    }

    @Override
    public ValidationItem validate(CommercialAutoRequest commercialAutoRequest) {
        return ValidationItem.builder().build();
    }
}
```

The implementation defined above can only be deployed if two configuration objects are also deployed: An account configuration named `CommercialAccount` and a product configuration named `CommercialAuto`.

Execution [#execution]

Once plugin implementations have been [deployed](/configuration/general-topics/deployment), the system will automatically execute plugin code when entities move to a specific stage of their lifecycles, in response to system events, or when calling certain API endpoints. See the feature guide for each plugin for more information.

Certain plugins are triggered when quotes and policy transactions move to a specific lifecycle state:

| State        | Plugins               |
| ------------ | --------------------- |
| Draft        | N/A                   |
| Validation   | Precommit, Validation |
| Pricing      | Rating                |
| Underwriting | Underwriting          |
| Accept       | N/A                   |
| Issue        | N/A                   |

Plugin Execution Context [#plugin-execution-context]

The `PluginExecutionContext` class provides all plugins with a set of built-in methods for retrieving metadata related to the current plugin execution.

It can be used to access contextual information associated with the current plugin execution, such as the tenant locator or user locator.

The `PluginExecutionContext` class contains the following methods:

* getRequestId()
* getTenantLocator()
* getBusinessAccount()
* getUserLocator()
* getUserRoles()

For example, the following code retrieves the `requestId`:

```java
public class ValidationPluginImpl implements ValidationPlugin {

    @Override
    public ValidationItem validate(CommercialAutoQuoteRequest commercialAutoQuoteRequest) {
        PluginExecutionContext context = PluginExecutionContext.get();
        String requestId = context.getRequestId();

        return ValidationItem.builder().build();
    }
}
```

<span id="PluginDataFetcher" />

Data Fetcher [#data-fetcher]

All plugins have access to the `DataFetcher` class, which can be used to retrieve data from a wide variety of entities, including quotes, policies, and accounts.

For example, the following code retrieves an account using an account locator:

```java
// Retrieve an account using an account locator

@Override
public ValidationItem validate(CommercialAutoQuoteRequest commercialAutoQuoteRequest) {
    CommercialAccount account = DataFetcherFactory.get().getAccount(commercialAutoQuoteRequest.quote().accountLocator());

    return ValidationItem.builder().build();
}
```

The following is a complete list of all methods available through the `DataFetcher` class:

| Method                              | Parameters                                             | Response                                                                                           |
| ----------------------------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| getAccount()                        | `accountLocator`                                       | <ApiLink name="AccountResponse" />                                                                 |
| getQuickQuote()                     | `quickQuoteLocator`                                    | <ApiLink name="QuickQuoteResponse" />                                                              |
| getQuote()                          | `quoteLocator`                                         | <ApiLink name="QuoteResponse" />                                                                   |
| getQuoteUnderwritingFlags()         | `quoteLocator`                                         | <ApiLink name="QuoteUnderwritingFlagsResponse" />                                                  |
| getQuotePricing()                   | `quoteLocator`                                         | <ApiLink name="QuotePriceResponse" />                                                              |
| getQuoteDocuments()                 | `quoteLocator`                                         | <ApiLink name="DocumentListResponse" />                                                            |
| getQuoteStaticData()                | `locator`                                              | `static`                                                                                           |
| getTransaction()                    | `transactionLocator`                                   | <ApiLink name="PolicyTransactionResponse" />                                                       |
| getTransactionUnderwritingFlags()   | `transactionLocator`                                   | <ApiLink name="TransactionUnderwritingFlagsResponse" />                                            |
| getTransactionPricing()             | `transactionLocator`                                   | <ApiLink name="TransactionPriceResponse" />                                                        |
| getDocumentsAttachedToTransaction() | `transactionLocator`                                   | <ApiLink name="DocumentListResponse" />                                                            |
| getAffectedTransactions()           | `transactionLocator`                                   | <ApiLink name="AffectedTransaction">AffectedTransaction\[]</ApiLink>                               |
| getPolicy()                         | `policyLocator`                                        | <ApiLink name="PolicyResponse" />                                                                  |
| getPolicyStaticData()               | `locator`                                              | `static`                                                                                           |
| getTerm()                           | `termLocator`                                          | <ApiLink name="TermResponse" />                                                                    |
| getTermCharges()                    | `termLocator`                                          | map\<`transactionLocator`, <ApiLink name="PolicyChargeResponse">PolicyChargeResponse\[]</ApiLink>> |
| getTermSubsegmentSummaries()        | `termLocator`                                          | StreamingEntity\<<ApiLink name="SubsegmentSummary" />>, excluding `documentSummary`                |
| getSegment()                        | `segmentLocator`                                       | <ApiLink name="SegmentResponse" />                                                                 |
| getSegments() **Deprecated**        | `transactionLocator`                                   | <ApiLink name="SegmentResponse">SegmentResponse\[]</ApiLink>                                       |
| getSegmentByTransaction()           | `transactionLocator`                                   | <ApiLink name="SegmentResponse" />                                                                 |
| getSegmentDocuments()               | `segmentLocator`                                       | <ApiLink name="DocumentListResponse" />                                                            |
| getAuxData()                        | `locator`, `key`                                       | <ApiLink name="AuxDataResponse" />                                                                 |
| getAuxDataKeys()                    | `locator`, `offset`, `count`                           | <ApiLink name="AuxDataKeySetResponse" />                                                           |
| getUnderwritingFlag()               | `underwritingFlagLocator`                              | <ApiLink name="UnderwritingFlagResponse" />                                                        |
| getDiaries()                        | `referenceType`, `referenceLocator`, `offset`, `count` | <ApiLink name="DiaryEntryResponse">DiaryEntryResponse\[]</ApiLink>                                 |
| getPreferences()                    | `transactionLocator`                                   | <ApiLink name="Preferences" />                                                                     |
| getInstallmentLattice()             | `installmentLatticeLocator`                            | <ApiLink name="InstallmentLatticeResponse" />                                                      |
| getInstallment()                    | `installmentLocator`                                   | <ApiLink name="Installment" />                                                                     |
| getInvoice()                        | `invoiceLocator`                                       | <ApiLink name="InvoiceResponse" />                                                                 |
| getInvoiceDetails()                 | `invoiceLocator`                                       | <ApiLink name="InvoiceDetailsResponse" />                                                          |
| getPayment()                        | `paymentLocator`                                       | <ApiLink name="PaymentResponse" />                                                                 |
| getDelinquencyEvents()              | `delinquencyLocator`, `offset`, `count`                | <ApiLink name="DelinquencyEventsResponse" />                                                       |
| getTask()                           | `taskLocator`                                          | <ApiLink name="Task" />                                                                            |
| getUserAssociation()                | `userAssociationLocator`                               | <ApiLink name="UserAssociation" />                                                                 |
| getFnol()                           | `fnolLocator`                                          | <ApiLink name="FnolResponse" />                                                                    |
| getFnolLosses()                     | `fnolLocator`                                          | <ApiLink name="FnolLoss">FnolLoss\[]</ApiLink>                                                     |
| getFnolClaims()                     | `fnolLocator`                                          | `claims`                                                                                           |
| getContact()                        | `locator`                                              | <ApiLink name="ContactRoles" />                                                                    |
| getQuoteContacts()                  | `quoteLocator`                                         | <ApiLink name="ContactRoles">ContactRoles\[]</ApiLink>                                             |
| getPolicyContacts()                 | `policyLocator`                                        | <ApiLink name="ContactRoles">ContactRoles\[]</ApiLink>                                             |
| getAccountContacts()                | `accountLocator`                                       | <ApiLink name="ContactRoles">ContactRoles\[]</ApiLink>                                             |
| getFnolContacts()                   | `fnolLocator`                                          | <ApiLink name="ContactRoles">ContactRoles\[]</ApiLink>                                             |

<span id="resource_selector" />

Resource Selector [#resource-selector]

All plugins have access to the `ResourceSelector` class, which can be used to retrieve data from [resources](/configuration/resources/versioned-resource-selection) such as tables and secrets.

The `ResourceSelector` automatically selects resource instances based on resource selection rules. See the [Versioned Resource Selection](/configuration/resources/versioned-resource-selection) feature guide for more information, including details on the `LockingResourceSelector`.

Data Tables [#data-tables]

The `ResourceSelector` can be used to retrieve data from [data tables](/configuration/resources/data-tables).

Here's an example of retrieving a record from a data table named `VehicleTypeFactor`:

```java
public class RatingPluginImpl implements RatePlugin {
   private static final Logger log = LoggerFactory.getLogger(RatingPluginImpl.class);

    // Retrieve a record from the VehicleTypeFactor table

    @Override
    public RatingSet rate(CommercialAutoQuoteRequest commercialAutoQuoteRequest) {
        CommercialAutoQuote commercialAutoQuote = commercialAutoQuoteRequest.quote();
        List<RatingItem> ratingItems = new ArrayList<>();

        Vehicle vehicle = commercialAutoQuote.vehicleSchedule().vehicles().stream().findFirst().orElseThrow();

        Optional<VehicleTypeFactor> record = ResourceSelectorFactory.getInstance()
                .getSelector(commercialAutoQuote)
                .getTable(VehicleTypeFactor.class)
                .getRecord(VehicleTypeFactor.makeKey(vehicle.data().vehicleMake(), vehicle.data().vehicleModel(), vehicle.data().vehicleYear()));

        BigDecimal rate = record.orElseThrow().rate();

        ratingItems.add(RatingItem.builder()
                .elementLocator(vehicle.locator())
                .chargeType(ChargeType.premium)
                .rate(rate)
                .build());

        return RatingSet.builder().ok(true).ratingItems(ratingItems).build();
    }
}
```

Here's an example of the `VehicleTypeFactor` data table:

| makeSymbol | modelSymbol | modelYear | typeFactor | collFactor |
| ---------- | ----------- | --------- | ---------- | ---------- |
| key        | key         | key       | value      | value      |
| Toyota     | Camry       | 2023      | 0.84221    | 0.86764    |
| Ford       | Explorer    | 2022      | 0.87839    | 0.91023    |

In the previous code example, a `CommercialAutoQuote` functions as the reference point for resource selection logic. However, you can also specify a time as the reference point instead.

For example:

```java
Optional<VehicleTypeFactor> record = ResourceSelectorFactory.getInstance()
        .getSelector(Instant.now())
        .getTable(VehicleTypeFactor.class)
        .getRecord(VehicleTypeFactor.makeKey(vehicle.data().vehicleMake(), vehicle.data().vehicleModel(), vehicle.data().vehicleYear()));
```

Range Tables [#range-tables]

The `ResourceSelector` can be used to retrieve data from [range tables](/configuration/resources/range-tables).

Here's an example of retrieving a record from a range table named `AdjustedRate`:

```java
Optional<AdjustedRate> record = ResourceSelectorFactory.getInstance()
        .getSelector(commercialAutoQuote)
        .getRangeTable(AdjustedRate.class)
        .getRecord(AdjustedRate.makeKey(), BigDecimal.TEN);
```

Here's an example of range table extrapolation:

```java
ResourceSelectorFactory.getInstance()
        .getSelector(commercialAutoQuote)
        .getRangeTable(VehicleStateFactor.class)
        .extrapolate(TableUtils.makeKey("Toyota", "California"), BigDecimal.valueOf(2016),
                VehicleStateFactor::factor, Interpolation.linear)
        .ifPresent(factor -> log.info("Extrapolated VehicleStateFactor for year {}: {}", 2016, factor));
```

Here's an example of range table interpolation:

```java
ResourceSelectorFactory.getInstance()
        .getSelector(commercialAutoQuote)
        .getRangeTable(VehicleStateFactor.class)
        .interpolate(TableUtils.makeKey("Toyota", "California"), BigDecimal.valueOf(2016),
                VehicleStateFactor::factor, Interpolation.linear)
        .ifPresent(factor -> log.info("Interpolated VehicleStateFactor for year {}: {}", 2016, factor));
```

Here's an example of retrieving the range table record for a key and range selection start that is closest to but not greater than the specified value:

```java
ResourceSelectorFactory.getInstance()
        .getSelector(commercialAutoQuote)
        .getRangeTable(VehicleStateFactor.class)
        .getLowerAdjacentRecord("exampleKey".getBytes(), BigDecimal.valueOf(108))
        .ifPresent(record -> log.info("Lower adjacent record: {}", record));
```

If multiple ranges match, the range with the lowest range selection end is returned.

Here's an example of retrieving the range table record for a key and range selection start that is closest to but not less than the specified value.

```java
ResourceSelectorFactory.getInstance()
        .getSelector(commercialAutoQuote)
        .getRangeTable(VehicleStateFactor.class)
        .getUpperAdjacentRecord("exampleKey".getBytes(), BigDecimal.valueOf(108))
        .ifPresent(record -> log.info("Upper adjacent record: {}", record));
```

If multiple ranges match, the range with the lowest range selection end is returned.

Constraints [#constraints]

The `ResourceSelector` can be used to retrieve [constraints](/configuration/data-extensions/data-extension-constraints).

For example:

```java
ConstraintsFetcher constraintsFetcher = ResourceSelectorFactory.getInstance()
        .getSelector(commercialAutoQuote)
        .getConstraints(ClassCodes.class);

Collection<String> constraints = constraintsFetcher.get("exampleKey".getBytes());
```

Secrets [#secrets]

Lastly, the `ResourceSelector` can be used to retrieve secrets by referencing the `staticName` of the secret.

Here's an example of a <ApiLink name="SecretRef">SecretRef</ApiLink> configuration object named `ExternalService`:

```json
{
	"items": {
		"url": {
			"dataType": "string"
		},
		"apiToken": {
			"dataType": "string"
		},
		"timeOutSeconds": {
			"dataType": "int"
		}
	}
}
```

Execute the `deployConfigToTenant` Gradle task to [deploy](/configuration/general-topics/deployment) your configuration.

Once these configurations have been deployed, create your secret using the <ApiLink name="createSecret">Create a Secret</ApiLink> API endpoint.

For example:

```json
{
	"name": "ExampleSecret",
	"staticName": "ExternalService",
	"secret": {
		"url": "https://example.url.com",
		"apiToken": "Bearer EXAMPLE_TOKEN",
		"timeOutSeconds": 1000
	}
}
```

Secrets must be added to at least one [resource group](/api/resources/resource-service) before they can be accessed through the `AutomationPluginContextData` class by using the <ApiLink name="createResourceGroup">Create a Resource Group</ApiLink> or <ApiLink name="updateResourceGroup">Update a Resource Group</ApiLink> API endpoint.

Here's an example request for the <ApiLink name="createResourceGroup">Create a Resource Group</ApiLink> API endpoint:

```json
{
	"name": "ExampleResourceGroup",
	"selectionStartTime": "2023-12-22T19:09:27+0000",
	"resourceNames": ["ExampleSecret"]
}
```

Here's an example request for the <ApiLink name="updateResourceGroup">Update a Resource Group</ApiLink> API endpoint:

```json
{
	"name": "ExampleResourceGroup",
	"selectionStartTime": "2023-12-22T19:09:27+0000",
	"addResources": ["ExampleSecret"]
}
```

Once the secret has been added to a resource group, execute the `refreshReferenceDatamodel` Gradle task to generate the Java class containing your secrets.

Secret fields can now be accessed through the `ResourceSelector`.

For example:

```java
Optional<ExternalService> secret = ResourceSelectorFactory.getInstance()
        .getSelector(commercialAutoQuote)
        .getSecret(ExternalService.class);

String url = secret.url();
String apiToken = secret.apiToken();
Integer timeOutSeconds = secret.timeOutSeconds();
```

See the [Versioned Resource Selection](/configuration/resources/versioned-resource-selection) feature guide for more information on resource selection logic.

Aux Data [#aux-data]

Aux data refers to general-purpose key-value pairs stored within the Socotra Insurance Suite. All plugins have access to aux data via the [Data Fetcher](/configuration/plugins/overview#data-fetcher).

The following aux data methods are available to all plugins via the Data Fetcher:

* getAuxDataKeys()
* getAuxData()
* setAuxData()
* deleteAuxData()

Here's a demonstration of each aux data method:

```java
@Override
public ValidationItem validate(CommercialAutoQuoteRequest commercialAutoQuoteRequest) {
    String quoteLocator = commercialAutoQuoteRequest.quote().locator().toString();

    // Parameters: getAuxDataKeys(locator, offsetValue, pageSize)
    AuxDataKeysSet keySet = DataFetcher.getInstance().getAuxDataKeys(quoteLocator, 0, 100);

    if (!keySet.auxDataKeys().isEmpty()) {
        String key = keySet.auxDataKeys().stream().findFirst().get().key();
        String value = DataFetcher.getInstance().getAuxData(quoteLocator, key).value();

        Collection<AuxDataSet> auxDataSets = new ArrayList<>();

        AuxDataSet createRequest = AuxDataSet.builder().key(key).value(value).build();

        auxDataSets.add(createRequest);

        createRequest = AuxDataSet.builder().uiType(UiType.normal).key("keyToDelete").value(value).build();

        auxDataSets.add(createRequest);

        AuxDataSetCreateRequest setCreateRequest = AuxDataSetCreateRequest.builder()
                .auxDataSettingsName("ShortExpire")
                .auxData(auxDataSets).build();

        AuxDataService.getInstance().setAuxData(quoteLocator, setCreateRequest);
        AuxDataService.getInstance().deleteAuxData(quoteLocator, "keyToDelete");
    }

    return ValidationItem.builder().build();
}
```

Events Service [#events-service]

The `EventsService` can be used to manually trigger [custom events](/configuration/general-topics/events#custom-events) within Socotra, allowing you to execute workflows via [webhooks](/configuration/general-topics/webhooks) or an event-driven implementation of the [Automation Plugin](/configuration/plugins/automation#creating-an-event-driven-automation-plugin-implementation).

For example:

```java
// Execute a custom event named ExampleEvent with payload data
EventsService.getInstance().createEvent(CustomEvent.ExampleEvent, Map.of("quote", quote.locator(), "info", "Plugin executed"));

// Execute a custom event named ExampleEvent without payload data
EventsService.getInstance().createEvent(CustomEvent.ExampleEvent);
```

Money Service [#money-service]

The `MoneyService` can be used to perform precise financial calculations based on a specified [currency code](https://www.iban.com/currency-codes).

Here's a demonstration of `MoneyService` functionality:

```java
// Create a MoneyService object for a specific currency such as US dollars
MoneyService moneyService = new MoneyService("USD");

BigDecimal amount = new BigDecimal("123.4567");

// Round the amount to the currency's default number of decimal places
BigDecimal roundedAmount = moneyService.toMoney(amount); // Result: 123.46

// Example of a rate calculation
BigDecimal totalAmount = new BigDecimal("1000.00");
BigDecimal duration = new BigDecimal("12.0");

// Calculate a rate for a target amount over a duration
BigDecimal rate = moneyService.getRateForTargetAmount(totalAmount, duration);
```

External API Calls [#external-api-calls]

All plugins can call external API endpoints via the [Java HttpClient](https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpClient.html).

For example:

```java
try (HttpClient client = HttpClient.newHttpClient()) {
    HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://example.com/some-data"))
            .GET()
            .build();

    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

    log.info("Response: {}", response.toString());

} catch (Exception e) {
    log.error("Exception: {}", e.toString());
}
```

<Callout>
  The Java HttpClient is the only HTTP client currently supported by the platform. The [Automation Plugin](/configuration/plugins/automation) is the only plugin that can call Socotra API endpoints directly.
</Callout>

Logging [#logging]

The [Logging API](/api/configuration-and-development/logging) can be used to view the history of plugin executions and Java logging messages.

First, retrieve a list of plugin executions using the <ApiLink name="fetchLogsList">Fetch a List of Logs</ApiLink> API endpoint. Optional query parameters can be used to narrow down this list.

For example, use the `createdAtMin` query parameter to view plugin executions that occurred after a specified timestamp:

```
createdAtMin=2026-07-25T22:00:00.000Z
```

Next, retrieve Java logging messages for a plugin execution by calling the <ApiLink name="fetchLogs">Fetch Logs for a Request</ApiLink> API endpoint and specifying the target <ApiLink name="PluginLogsMetadata">PluginLogsMetadata</ApiLink> `locator` as the `locator` for the request.

<Callout>
  Plugin logs are only stored for 30 days.
</Callout>

Plugin Restrictions [#plugin-restrictions]

The following restrictions apply to all classes that implement a Socotra plugin interface:

* Classes cannot be declared `abstract` and must be instantiable.
* Classes cannot have a constructor with one or more arguments. A no-argument constructor is allowed and will be generated automatically if not provided.

The system will block the deployment of plugin classes that violate these rules.

Each plugin is executed in an isolated memory space. Accessing plugin fields or calling methods belonging to a different plugin is not supported and should not be attempted.

Next Steps [#next-steps]

* [Precommit Plugin](/configuration/plugins/precommit)

See Also [#see-also]

* [Configuration Deployment](/configuration/general-topics/deployment)
* [Versioned Resource Selection](/configuration/resources/versioned-resource-selection)
* [Java HttpClient](https://docs.oracle.com/en/java/javase/21/docs/api/java.net.http/java/net/http/HttpClient.html)
