Product module code

Configure custom product logic in JavaScript

What is product module code?

Product module code is a low-code environment in which custom logic can be written for insurance products on the Root platform.

While some product functionality is configured in the general product settings, product module code allows you to build more complex business rules in JavaScript. This offers a large degree of flexibility in customising a product module to meet product requirements.

The platform references the product module code to execute customised functions (or hooks) relating to a specific event for your product. For example, when the quote API endpoint is called for your product, the platform will look in your product module code for the validateQuoteRequest() and getQuote() functions to execute.

Product module code is contained in multiple virtual JavaScript files. The published code is run by the platform in a secured Node.js virtual environment. Specific built-in Node.js globals, as well as external modules for dates, validation and API requests, are made available in this environment.

Root also enables a set of SDK methods to retrieve policy and related information directly from the platform's runtime environment, without having to route requests through the externally exposed API endpoints.

📘

Type checking in JavaScript

By default, we add a VS Code setting to your product module code directory to enable type checking. This allows you to leverage some of TypeScript's type checking and error reporting functionality in regular JavaScript files - a great way to catch common programming mistakes.

These type checks use the type declaration files included under code > types and code > unit-tests > types.

To turn off type checking, open the relevant jsconfig.json file and set compilerOptions.checkJs to false.

Helper functions

Aside from the specific functions that correspond to individual hooks, you may need to define a wide variety of your own helper functions and constants to make the product module code efficient, readable, maintainable and testable. This is in the form of abstracted JavaScript functions. Helper functions make it easy to share logic with other hooks in the product module code.

Pricing and charges helpers

The platform relies on the getQuote() function to generate a quote. This means the product module code will typically include functions to calculate the premium based on the rating factors (parameters relevant to pricing) received via the quote endpoint.

This could also include calculating variable cover amounts and other policy-specific information, depending on how the product has been designed to work.

Validation helpers

Helper functions can also be useful to perform additional validations. In some cases Joi validation schemas are not ideal for performing complex validations, especially where the validation of some parameters depend, in complex ways, on the value of other parameters. In these cases helper functions can be used to define custom validation of the input data.

To return error messages via the API in case of invalid input, the command throw new Error('<Error message>'); can be used in the product module code. This will halt execution and result in a 400 Bad Request API response.

Miscellaneous helpers

Examples of other helper functions could be a function to calculate the policyholder's age from her ID number, or a function to merge data received at the quote and application stage into a single object.

Examples of constants could be a constant percentage to calculate commissions, or an array containing a list of valid country codes.

Environment variables

You can access the following Root-specific environment variables related to the context in which the product module code is run. This is useful when you need to execute environment-specific code, such as calling the staging or production instance of an external service depending on whether the product module code is executed for a sandbox or live policy.

PropertyDefinition
process.env.ENVIRONMENTString. One of [sandbox, production]. See the Overview guide for more details.
process.env.ORGANIZATION_IDString. The UUID of the organization. For example, for product module code hooks that are triggered by an API call, this will be the organization linked to the API key.

Root globals

When the product module code is run by the platform, various classes and modules are passed to the virtual machine environment as globals. These globals can be referenced in the product module code without defining them explicitly.

Returned objects

The classes in the table below can be accessed as globals in the product module code. Specific functions in the product module code related to policy issuing and other lifecycle events return instances of these classes. For example, the getPolicy() function returns an instance of the Policy class. Typically, these response objects are also returned in JSON format via the corresponding API endpoint.

Class nameGuideAPI reference
QuotePackageQuote hookQuote package object
ApplicationApplication hookApplication object
PolicyPolicy issue hookPolicy object
AlterationPackageAlteration hooksAlteration package object
AlteredPolicyAlteration hooksSee the guide.
ReactivationOptionReactivation hookSee the guide.
RequotePolicy (deprecated)Requote hookRequote policy endpoint

📘

Throwing errors

To throw an error from the product module code, simply throw a standard JavaScript error. For example, throw new Error('Policyholder date of birth does not match age provided at quote step).

The error message you specified will be returned in the API response, will be visible in the product module execution logs, and may be displayed to on the dashboard, depending on the context.

UUIDs

The following function generates a universally unique identifier (UUID).

const uuidString = createUuid();

External modules

Built-ins

The following built-in JavaScript modules can be accessed in the product module code:

  • Math - Properties and methods for mathematical constants and functions. Useful, for example, for rounding currency values to the nearest integer in the product module code
  • JSON - Methods for parsing JavaScript Object Notation (JSON) and converting values to JSON. Useful, for example, for making external API calls from the product module code.
  • Promise - Representing the eventual completion (or failure) of an asynchronous operation and its resulting value. Useful, for example, for making external API calls and calling the Root SDK methods.

Validation

Root enables the popular and powerful Joi validation library in the product module code. This is particularly useful for validating the product-specific input parameters received via your product's quote, application and alteration endpoints.

Note:

  • Root currently supports version 11.3.4 of Joi. Methods released in later versions of Joi are not currently supported.
  • The Joi.date() and Joi.string().isoDate() methods have been customised by Root and therefore won’t work exactly as per the Joi docs. Please refer to the list of extensions below.

The Joi module used in the product module code includes a number of custom Root extensions for insurance applications. The available extensions are listed below.

Dates

Normally when you use the Joi.date() and Joi.string().isoDate() methods to validate inputs and the date input doesn't explicitly include the timezone offset, the offset is assumed to be UTC.

Root has customised these methods to use the organisation's timezone as the default if the timezone offset is not explicitly included in the date input being validated.

To override the timezone offset used as the default for the date input, you can either explicitly include the timezone offset in the date input (e.g. 2022-12-15T00:00:00+02:00) or you can pass the timezone as a parameter in the joi method schema (list of supported timezones).

// Organisation timezone used as default
const dateSchema = Joi.date();
const isoDateSchema = Joi.string().isoDate();

// New York timezone used as default
const dateSchema = Joi.date('America/New_York');
const isoDateSchema = Joi.string().isoDate('America/New_York');

ID number

This extension checks whether a string is a valid South African ID number.

const joiIdNumberSchema = Joi.string().idNumber();

IMEI

This extension checks whether a string is a valid 15-digit IMEI number. Dashes (the - character) are not allowed.

const joiImeiSchema = Joi.string().imei();

Digits

This extension checks whether a string contains only digits between 0 - 9.

const joiDigitsSchema = Joi.string().digits();

JSON string

This extension checks whether a string represents valid JavaScript Object Notation (JSON).

Note: This method does not allow single ' quotes. Only escaped double quotes are allowed, e.g. { \"key\": \"value\" } not { 'key': 'value' }.

const joiJsonSchema = Joi.string().jsonString();

RFC email

This extension checks whether a string conforms to the RFC 5322 standard for email addresses.

const joiRfcEmailSchema = Joi.string().rfcEmail();

📘

Validation of contact details does not guarantee correct input

Joi validation extensions like RFC email and cellphone are only a "first line of defence" against erroneous data input. The fact that these validation checks are passed does not guarantee that the correct data has been provided by the customer, or that this data was correctly captured (for example by a call centre agent).

Cellphone

Checks that a string is a valid South African phone number.

const joiCellphoneSchema = Joi.cellphone();

Date of birth

This extension checks the following:

  • The value must be a string.
  • The value must be parseable as a date in the specified format. If no format is specified, "YYYYMMDD" and "YYYY-MM-DD" will be attempted.
  • The date must be equal to or earlier than the start start of the day the code is executed.
const joiDateOfBirthSchema = Joi.dateOfBirth().format("YYYY-MM-DD");

Dates

Root enables the moment.js library for working with dates in the product module code. This is useful, for example, for manipulating and setting policy start and end dates. Root currently supports version 2.29.4 of moment.js.

Note: The moment module available in the product module code is in UTC mode. This means that dates will be represented in and timezone offsets assumed as UTC. Please refer to the parsing docs and guide for more information.

const getPolicy = (application, policyholder) => {
	// Function body omitted
  return new Policy({
    start_date: moment().format(),
    end_date: moment().add(1, 'year').format(),
    // Other properties omitted
  });
}

Root also enables the moment-timezone extension which allows you to parse and display dates in any timezone. Root currently supports version 0.5.40 of moment-timezone.

const getPolicy = (application, policyholder) => {
    // Function body omitted
  return new Policy({
    start_date: momentTimezone().tz('America/New_York').format(),
    end_date: moment().tz('America/New_York').add(1, 'year').format(),
    // Other properties omitted
  });
}

External API calls

Root enables the use of the node-fetch module for making API calls from within the product module code. This is a Node.js implementation of the JavaScript fetch API. Root currently supports version 2.6.0 of node-fetch.

const getNewPremium = async ({ requestBody }) => {
  const response = await fetch('https://domain.client.co.za/premium-engine'
    body: JSON.stringify(requestBody),
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Basic ${API_KEY}`,
    },
  });

  const json = await response.json();
  checkForAPIErrors({ response, json });
  return json;
};

Root SDK methods

The following methods can be used within the product module code to retrieve policy and related platform data from within the platform's runtime environment.

Policies and payments

root.policies.getPolicy(policyId)

This method can be used to retrieve a policy using its policy_id (a string representing a UUID). This could be useful, for example, to retrieve information from an earlier policy issued to the same policyholder.

Parameters

  • policyId: String. Required. The UUID of the policy to be retrieved.

Returns

  • A Promise that resolves to a Policy object.

Example

const policy =  await root.policies.getPolicy('ec62de5e-354b-4e2e-96ec-ecf358da6d53');

root.policies.getPolicies({ filters?, pagination? })

This method can be used to retrieve a policy using the 10-digit policy number that was created when the policy was issued, or the identification or passport number of the policyholder. This could be useful, for example, to retrieve information from an earlier policy issued to the same policyholder.

Note: Using both the filters and pagination parameters at the same time can result in unexpected behaviour. These parameters should be used in the alternative - if one is included, the other should be excluded.

Parameters

  • params: Object. Required.
    • filters: Object. Optional.
      • policyNumbers: Array<String>. Optional. A list of policy numbers to filter for.
      • policyholderIdentificationNumber: String. Optional. A valid RSA ID or passport number to filter for. Only policies linked to the policyholder with that identification number will be returned.
    • pagination: Object. Optional.
      • limit: Integer. Optional. The maximum number of policies to return. Defaults to 1000, which cannot be exceeded.
      • offset: Integer. Optional. The (zero-based) index of the first item in the collection to return

Returns

  • A Promise that resolves to an array of Policy objects.

Examples

const policies =  await root.policies.getPolicies({
  filters: { policyNumbers: ['C8E819EEA7']},
});

const policies =  await root.policies.getPolicies({
  filters: { policyholderIdentificationNumber: '9201235036089'},
});

root.policies.getPolicyEvents(policyId)

This method can be used to retrieve all the policy events related to an existing policy. These events include policy status changes (e.g. lapsing or cancelling the policy), policy data updates, and claims being linked to the policy, among others.

Parameters

  • policyId: String. Required. The UUID of the policy for which to retrieve its events.

Returns

  • A Promise that resolves to an array of policy event objects.

Example

const policyEvents =  await root.policies.getPolicyEvents('ec62de5e-354b-4e2e-96ec-ecf358da6d53');

root.policies.getPolicyPayments(policyId, params?)

This method can be used to retrieve all payments related to an existing policy. Retrieving the full payment history from within the product module code can be useful, for example, for specifying product-specific lapse rules.

Note: Using both the filters and pagination parameters at the same time can result in unexpected behaviour. These parameters should be used in the alternative - if one is included, the other should be excluded.

Parameters

  • policyId: String. Required. The UUID of the policy for which to retrieve its payments.
  • params: Object. Optional.
    • filters: Object. Optional.
      • statuses: Array<String>. Optional. A list of payment statuses to include in the query. One or more of ['pending', 'submitted', 'processing', 'failed', 'successful', 'cancelled']. If this parameter is omitted, all payment statuses will be included.
      • paymentDateFrom: String. Optional. ISO date after which to return payments.
    • pagination: Object. Optional.
      • limit: Integer. Required (optional if offset is specified). The maximum number of events to return. Cannot exceed 1000.
      • offset: Integer. Required (optional if limit is specified). The (zero-based) index of the first item in the collection to return.

Returns

  • A Promise that resolves to an array of Payment objects.

Example

const policyPayments =  await root.policies.getPolicyPayments(
  'ec62de5e-354b-4e2e-96ec-ecf358da6d53', 
  {
    filters: { 
      statuses: ['processing', 'successful'],
      paymentDateFrom: '2021-01-01',
    },
    pagination: {
      limit: 10,
      offset: 10,
    },
  }
);

root.policies.countPolicyPayments(policyId, params?)

This method can be used to retrieve the number of payments on a policy. This is useful for paginating the getPolicyPayments() SDK method.

Parameters

  • policyId: String. Required. The UUID of the policy for which to retrieve the payment count.
  • params: Object. Optional.
    • filters: Object. Optional.
      • statuses: Array<String>. Optional. A list of payment statuses to include in the query. One or more of ['pending', 'submitted', 'processing', 'failed', 'successful', 'cancelled'].
      • paymentDateFrom: String. Optional. ISO date after which to return the payment count.

Returns

  • A Promise that resolves to an integer.

Example

const policyPaymentCount =  await root.policies.countPolicyPayments(
  'ec62de5e-354b-4e2e-96ec-ecf358da6d53', 
  { 
    filters: { 
      statuses: ['processing', 'successful'],
      paymentDateFrom: '2021-01-01',
    },
  }
);

Payment coupons

root.policies.getPaymentCoupons(params)

This method can be used to retrieve all the payment coupons for the given policy.

Parameters

  • params: Object. Required.
    • policyId: String. Required. The UUID string of the policy for which the coupons are to be retrieved.
    • includes: Object. Optional.
      • policy. Boolean. Optional.
    • filters: Object. Optional.
      • status: Array<String>. Optional. A list of payment coupon statuses to filter for. One or more of pending, redeemed, cancelled, and reversed.
      • type: Array<String>. Optional. A list of payment coupon types to filter on. One or more of ad_hoc and payment_holiday.
      • redeemableOn: String. Optional. A date, for example with a format of YYYY-MM-DD, on which the coupons should be redeemable. This should only be used for payment_holiday coupons.
      • expiredOn: String. Optional. A date, for example with a format of YYYY-MM-DD, at which the coupons should have expired by. This should only be used for payment_holiday coupons.
      • updatedAfter: String. Optional. A date, for example with a format of YYYY-MM-DD, after which the coupons should have been updated.
      • updatedTo: String. Optional. A date, for example with a format of YYYY-MM-DD, up to which the coupons could have been updated.

Returns

  • A Promise that resolves to an array of the retrieved Payment Coupon objects. This Promise can be resolved with the .then() and .catch() Promise methods or with await in an async function.

Example

const paymentCoupons = await getPaymentCoupons({
  policyId: 'ec62de5e-354b-4e2e-96ec-ecf358da6d53',
  includes: { policy: true },
  filters: {
    status: ['pending', 'cancelled'],
    type: ['payment_holiday'],
    redeemableOn: '2021-02-15',
    expiredOn: '2021-03-01',
    updatedAfter: '2021-01-15',
    updatedTo: '2021-02-20',
  },
});

root.policies.createPaymentCoupons(params)

This method can be used to create new payment coupons.

Parameters

  • params: Object. Required.
    • policyId: String. Required. The UUID string of the policy for which the coupons are to be created.
    • newPaymentCoupons: Array<Object>. Required. A list of payment coupon objects to be created.
      • type: String. Required. Either ad_hoc or payment_holiday.
      • redeemableFrom: String. Required for a type of payment_holiday. The date from which the coupon is redeemable.
      • redeemableTo: String. Required for a type of payment_holiday. The date up to which the coupon is redeemable.
      • amount: Number. Required for a type of ad_hoc.The currency amount that will be credited on the policy ledger when the coupon is redeemed. This is only used with ad_hoc payment coupons. For payment_holiday coupons, the ledger is credited with the policy premium when the coupon is redeemed.
      • reason: String. Optional. The reason for the payment coupon being created.

Returns

  • A Promise that resolves to an array of the created Payment Coupon objects. This Promise can be resolved with the .then() and .catch() Promise methods or with await in an async function.

Example

const paymentCoupons = await createPaymentCoupons({
  policyId: 'ec62de5e-354b-4e2e-96ec-ecf358da6d53',
  newPaymentCoupons: [
    {
      type: 'ad_hoc',
      amount: 4000,
    },
    {
      type: 'ad_hoc',
      amount: 5000,
      reason: 'Campaign',
    },
    {
      type: 'payment_holiday',
      redeemableFrom: '2021-02-01',
      redeemableTo: '2021-02-31',
    },
    {
      type: 'payment_holiday',
      redeemableFrom: '2022–05-01',
      redeemableTo: '2022-06-31',
      reason: 'Promotion',
    },
  ]
});

root.policies.cancelPaymentCoupon(params)

This method can be used to cancel payment coupons.

Parameters

  • params: Object. Required.
    • paymentCouponId: String. Required. The UUID string of the coupon to be canceled.

Returns

  • A Promise that resolves to the canceled Payment Coupon object. This Promise can be resolved with the .then() and .catch() Promise methods or with await in an async function.

Example

const paymentCoupons = await cancelPaymentCoupon({
  paymentCouponId: 'cef52505-8506-44b1-b623-2ef8c6fefa16',
});

root.policies.redeemPaymentCoupon(params)

This method can be used to redeem payment coupons.

Parameters

  • params: Object. Required.
    • paymentCouponId: String. Required. The UUID string of the coupon to be redeemed.
    • action: Object. Required.
      • paymentDate: String. Required. The payment date for which the coupon is being redeemed.
      • billingDate: String. Optional. The billing date for which the coupon is being redeemed.

Returns

  • A Promise that resolves to the redeemed Payment Coupon object. This Promise can be resolved with the .then() and .catch() Promise methods or with await in an async function.

Example

const paymentCoupons = await redeemPaymentCoupon({
  paymentCouponId: 'cef52505-8506-44b1-b623-2ef8c6fefa16',
  action: { paymentDate: '2021-02-15' },
});

root.policies.reversePaymentCoupon(params)

This method can be used to reverse payment coupons.

Parameters

  • params: Object. Required.
    • paymentCouponId: String. Required. The UUID string of the coupon to be reversed.

Returns

  • A Promise that resolves to the reversed Payment Coupon object. This Promise can be resolved with the .then() and .catch() Promise methods or with await in an async function.

Example

const paymentCoupons = await reversePaymentCoupon({
  paymentCouponId: 'cef52505-8506-44b1-b623-2ef8c6fefa16',
});

Data stores

root.dataStores.store(key).find()

This method can be used to retrieve a data store's entities. This is how data store data is retrieved from inside the product module code. See the data stores guide (work in progress) for more details

Parameters

  • key: String. Required. The key of the datastore to retrieve.

Returns

  • A Promise that resolves to an array of data store entities.

Example

const dataStoreEntities =  await root.dataStores.store('data_store_key').find();

Notifications

root.notifications.triggerCustomEvent(params)

This method can be used to trigger a custom notification event. See the custom notification events guide for more details.

Parameters

  • params: Object. Required.
    • customEventKey: String. Required. The key identifying the custom event to trigger.
    • customEventType: String. Required. The type of custom event. Must match the type on the Root management dashboard. One of [policy, payment_method, payment, claim].
    • id: String. Required. The UUID of the entity for which to trigger the event. If customEventType is policy or payment_method, it must be a policy_id. If customEventType is claim, it must be a claim_id. If customEventType is payment, it must be a payment_id.

Returns

  • Void

Example

await root.notifications.triggerCustomEvent({
  customEventKey: 'policyholder_birthday',
  customEventType: 'policy',
  id: policy.policy_id,
});