Lifecycle hooks
Automate actions for existing policies triggered by predefined events
Overview
Lifecycle hooks allow you to run custom logic and specify actions to be performed by the platform when certain predefined events in the lifecycle of a policy are triggered.
These lifecycle events include when payments succeed or fail on a policy, when a policy is reactivated, when a claim is approved, and many more touch points. Within these hooks, actions can be queued to execute, such as updating the policy and its module data, changing the policy's status, crediting or debiting the premium ledger of the policy, or triggering a custom notification.
Each lifecycle hook is specified using a corresponding function in the product module code. This allows you not only to link a set of actions to a specific policy event, but also to define complex logic to configure the conditions under which each action should be queued.
Function definition
You need to specify the following parts of the lifecycle hook function.
- Name - The name of the function determines which policy lifecycle event it will "hook into". The name must match one of the supported lifecycle hooks.
- Parameters - Each lifecycle hook accepts a single
params
object as an argument. The policy object and policyholder object are passed to all lifecycle hooks as properties ofparams
. (Note: this guide refers to the properties ofparams
as the function parameters.) Certain lifecycle hooks also accept additional arguments related to the event that triggered the hook. - Body - The body of the function can be used to define custom logic specifying the conditions under which certain actions are returned.
- Return statement - To execute an action or actions, the function must return an array of action objects, which are queued to be executed by the platform. See the full list of actions guide. (The return statement can be omitted if no actions are required, for example if the function only throws an error when certain conditions are met.)
const afterPolicyIssued = (params) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
Example
The example below uses the afterPolicyIssued
hook to define custom logic for whether a policy is activated on issue. If the next policy billing date is within 30 days of the policy issue date, the policy is activated directly after it is issued. Otherwise, the policy will be issued with the default status of pending_initial_payment
.
const afterPolicyIssued = ({ policy, policyholder }) => {
const now = moment();
const dayNumber = now.date();
const billingDay = policy.billing_day;
let nextBillingDate;
if (billingDay < dayNumber) {
nextBillingDate = now.clone().add(1, 'month').date(billingDay);
} else {
nextBillingDate = now.clone().date(billingDay);
}
// If next billing day is in less than 30 days, activate the policy
if (!(nextBillingDate.diff(now, 'days') <= 30)) {
return;
}
return [
{
name: 'activate_policy',
},
];
};
Policy
These hooks are triggered by policy changes.
After policy issued
This hook is triggered when a policy is issued. Policies are issued via the policy issue endpoint. This endpoint can also be called by frontend users via the Root management dashboard.
const afterPolicyIssued = ({ policy, policyholder }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After policy activated
This hook is triggered when a policy is activated. Policies are activated through varying events.
const afterPolicyActivated = ({ policy, policyholder }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After policy not taken up
If not taken up checks are enabled in the general product settings, a policy's status will automatically be changed to not_taken_up
if:
- no payments have been collected successfully for that policy, and
- the period of time since the last failed payment exceeds the specified grace period.
A policy can also be marked as not taken up by returning the mark_policy_not_taken_up
action in the product module code.
const afterPolicyNotTakenUp = ({ policy, policyholder }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After policy lapsed
A policy lapses according to the lapse rules defined in the general product settings. The policy status will be changed to lapsed
if:
- at least one payment has been collected successfully since the policy was issued, and
- the period of time since the last failed payment exceeds the grace period defined for the policy.
Policies can also be lapsed via the lapse policy endpoint, or by returning the lapse_policy
action in the product module code.
const afterPolicyLapsed = ({ policy, policyholder }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After policy cancelled
Policies are cancelled via the cancel policy endpoint. This changes the policy status to cancelled
. A policy cancellation can also be initiated from the Root management dashboard.
const afterPolicyCancelled = ({ policy, policyholder }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After policy expired
Policies are expired when they reach the end date on a policy. This changes the policy status to expired
.
const afterPolicyExpired = ({ policy, policyholder }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
Before policy reactivated
Reactivating a policy means changing the status of an inactive policy (for example a cancelled or lapsed policy) to active. Reactivation can be enabled or disabled in the general product settings.
If reactivation has been enabled, policies can be reactivated via the reactivate policy endpoint. Reactivation can also be initiated by frontend users from the Root management dashboard.
Since this hook is triggered before the policy status is changed, it can be used to prevent reactivation if certain conditions are not met. This is achieved by throwing an Error
from within the function, which will prevent reactivation from being completed.
The function takes both the policy
object and the reactivationOption
object as parameters. Read more in the reactivation hook guide.
const beforePolicyReactivated = ({ policy, policyholder, reactivationOption }) => {
if (<condition under which reactivation should be prevented>) {
throw new Error('This policy cannot be reactivated');
}
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After policy reactivated
This lifecycle hook is analogous to beforePolicyReactivated
, except that it is triggered after policy reactivation has been completed.
const afterPolicyReactivated = ({ policy, policyholder, reactivationOption }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
Before policy start date changed
The start date of a policy can be changed via the Root management dashboard, if this feature is enabled in the product module settings. Changing the start date of a policy means that the billing history will be recalculated with respect to the new start date.
This hook can be used to cancel the change in start date if certain conditions are not met. This is achieved by throwing an Error
from within the function, which will prevent start date change from being implemented.
The function takes both the policy
object and the billingChange
object as parameters.
const beforePolicyStartDateChanged = ({ policy, policyholder, billingChange }) => {
if (<condition under which start date change should be prevented>) {
throw new Error(`This policy's start date cannot be changed`);
}
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
Example of the billingChange
object:
{
"defer_update": false,
"billing_day": 1,
"start_date": "2022-01-01T00:00:00.000Z",
"ledger_adjustments": [
{
"policy_id": "0a4c9044-856b-4584-a0d0-5ba97d44d124",
"organization_id": "00000000-0000-0000-0000-000000000001",
"environment": "sandbox",
"amount": -17041,
"description": "Adjustment for start date change",
"currency": "ZAR"
}
],
"current_balance": -998,
"monthly_premium": 2577
}
The billingChange
object has the following properties:
Property | Definition |
---|---|
start_date | string. The new start date for the policy. |
billing_day optional | number. The new billing day for the policy. |
defer_update | boolean. Specifies whether the new start date will be applied to the policy immediately, or deferred to a later date. |
defer_update_until optional | string. The date on which the start date change will be applied, if the policy start date is going to be deferred. |
billing_month optional | number. The new billing day for the policy if it is an annual policy. |
ledger_adjustments | array. An array of ledger entries that will be added to to policy after the start date changes. Please see the "Ledger adjustments" table. |
current_balance | number. The current policy balance in cents. |
monthly_premium | number. The policy monthly premium in cents. |
Each object in the ledger_adjustments
array has the following properties:
Property | Definition |
---|---|
policy_id | string. The UUID of the policy to which the ledger adjustment applies. |
organization_id | string. The UUID of the organisation under which the policy was issued. |
environment | string. One of ] { "data": { "h-0". |
amount | integer. The amount of the ledger adjustment in cents. |
description optional | string. The reason for the ledger adjustment. |
currency | string. The three-digit code of the currency in which the policy is billed (e.g. USD ). |
balance optional | integer. The resulting balance on the policy ledger, in cents. |
After policy start date changed
This lifecycle hook is analogous to beforePolicyStartDateChanged
, except that it is triggered after policy start date has been changed. This hook can be used to generate any side-effects, such as making further updates to the policy module data as a result of the start date change.
const afterPolicyStartDateChanged = ({ policy, policyholder, billingChange }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After alteration package applied
This lifecycle hook is triggered after an alteration package is applied to a policy. To see which properties are included on the alteration package object, go to the API reference for the "Apply alteration package to policy" endpoint and click on the "Success" response.
Read more about how alteration hooks are configured in the alteration hooks guide.
const afterAlterationPackageApplied = ({
policy,
policyholder,
alteration_package,
alteration_hook_key,
}) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
Payments
These hooks are triggered by payment events, such as a successful payment collection, a payment collection failing, or a previous payment being reversed. Read more about how these payment events are defined in the billing and payments guide (work in progress).
The payment event hooks accept a policy object and a payment object as arguments.
After payment success
A payment is marked with a successful
status five days after the payment was submitted, provided that Root has not received a failed payment update from the payments provider within those five days.
const afterPaymentSuccess = ({ policy, policyholder, payment }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After payment failed
A payment is marked with a failed
status if Root receives a notification, saying that the payment failed to be collected, from the payments provider within five days of the payment being submitted.
If the failed payment notification is received after five days from the payment submission date, the original payment is still marked as successful
. To correct the outstanding balance on the policy, a payment reversal is created to debit the policy's ledger with the original payment amount.
const afterPaymentFailed = ({ policy, policyholder, payment }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After payment reversed
A payment reversal is a completely new, unique payment object that negates a previous payment amount. It will have a payment_type
of reversal
, and a field called reversal_of_payment_id
which stores a reference to the original (reversed) payment. The payment amount on the payment reversal will be negative.
This payment object representing the reversal is passed to the afterPaymentReversed()
function, together with the policy object.
const afterPaymentReversed = ({ policy, policyholder, payment }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
Payment coupons
Payment coupons allow you to credit a policy ledger for scenarios such as reward programs or schedule payment holidays for policies. For more information on creating coupons, have a look at the API.
Before payment coupon created
There is often a need for rules to govern when payment coupons may be created for a policy.
Since this hook is triggered before the payment coupon is created, it can be used to prevent the coupons from being created if certain conditions are not met. This is achieved by throwing an Error
from within the function.
The function takes both the policy
object and the newPaymentCoupons
array of coupons as parameters.
const beforePaymentCouponCreated = ({ policy, policyholder, newPaymentCoupons }) => {
// Custom logic
if (<condition under which creating payment coupons should be prevented>) {
throw new Error(`Payment coupons cannot be created for this policy`);
}
return [ ... ]; // Optional list of actions to queue
}
After payment coupon created
After a payment coupon has been created, the payment coupon is passed to the afterPaymentCouponCancelled
function, together with the policy
object.
const afterPaymentCouponCreated = ({ policy, policyholder, paymentCoupons }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After payment coupon cancelled
After a payment coupon has been canceled, the payment coupon is passed to the afterPaymentCouponCancelled
function, together with the policy
object.
Payment coupons are canceled via the cancel payment coupon endpoint (for a single payment coupon) or the cancel payment coupons endpoint (to cancel all payment coupons on a policy). Payment coupons can also be canceled from the Root management dashboard.
const afterPaymentCouponCancelled = ({ policy, policyholder, paymentCoupons }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After payment coupon redeemed
After a payment coupon has been redeemed, the payment coupon is passed to the afterPaymentCouponRedeemed
function, together with the policy
object.
const afterPaymentCouponRedeemed = ({ policy, policyholder, paymentCoupons }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After payment coupon reversed
Payment coupons can be reversed, changing their status from pending
to reversed
.
After a payment coupon has been reversed, the payment coupon is passed to the afterPaymentCouponReversed
function, together with the policy
object.
const afterPaymentCouponReversed = ({ policy, policyholder, paymentCoupons }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
Claims
These hooks are triggered by events related to claims. Read more about claims and how claims blocks work in the claims guide.
The claim hook functions accept the policy object linked to the claim, and the claim object itself as arguments.
After policy linked to claim
A claim is linked to a policy via the link a policy endpoint.
const afterPolicyLinkedToClaim = ({ policy, policyholder, claim }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After claim block updated
Claim information is stored in claims blocks, and specifically the claim block state. Read more about how claims block states work in the claims guide.
These blocks can be updated either via the update a block state endpoint (for a single block), or the update multiple block states endpoint. Claim blocks can also be updated directly in the user interface on the Root management dashboard.
const afterClaimBlockUpdated = ({ block_key, policy, policyholder, claim }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
Avoid infinite loops in claims blocks
The
afterClaimBlockUpdated
hook is triggered by a change of state of any of the blocks defined in the claim blocks schema. This means an infinite loop can be created unintentionally by updating a claim block state via the API from within theafterClaimBlockUpdated()
function.Updating claim block states from within the
afterClaimBlockUpdated()
function should be avoided where possible. If this cannot be avoided, you will need to define the logic in the body of theafterClaimBlockUpdated()
function carefully to prevent an infinite loop from being created.You can verify that no infinite loop has been created by checking the Execution Logs under the Product Module inside the Workbench home on the Dashboard. To stop an infinite loop in progress, you can close the claim.
After claim sent to review
After the claim is sent to review with the assessor (send-to-review), this lifecycle hook is triggered.
const afterClaimSentToReview = ({ policy, policyholder, claim }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
After claim decision
After the claim goes to the assessor for a decision (send-to-review) and a decision is made (approve, repudiate, goodwill, no claim), this lifecycle hook is triggered.
To know which decision was made by the assesor, you can reference claim.approval_status
, which will be one of ["approved", "repudiated", "goodwill", "no_claim"]
.
const afterClaimDecision = ({ policy, policyholder, claim }) => {
if (claim.approval_status === 'approved') {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
}
After claim decision acknowledged
After the claim assessor decision (approve, repudiate, goodwill, no claim), the claim is sent to the claims supervisor for review. If the decision is acknowledged (acknowledge approved, acknowledge repudiated, acknowledge goodwill, or acknowledge no claim) by the claims supervisor, this lifecycle hook is triggered.
To know which decision was acknowledged by the supervisor, you can reference claim.approval_status
, which will be one of ["approved", "repudiated", "goodwill", "no_claim"]
.
const afterClaimDecisionAcknowledged = ({ policy, policyholder, claim }) => {
if (claim.approval_status === 'approved') {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
}
After claim closed
After the claim is closed (close), this lifecycle hook is triggered.
const afterClaimClosed = ({ policy, policyholder, claim }) => {
// Custom logic
return [ ... ]; // Optional list of actions to queue
}
Updated 9 days ago