Restricted data

Overview

Root allows your product module code to store sensitive, product-specific data on quotes, applications, and policies using the restricted_data field. This field works alongside the standard module field, but its visibility in API responses is controlled by permissions — allowing you to hide sensitive information from certain API consumers.

Common use cases for restricted data include:

  • Underwriting details that should not be visible to end users or embedded flows.
  • Internal pricing breakdowns such as risk scores, reinsurance premiums, or loss ratios.
  • Compliance-related fields that only authorised back-office users should access.

The restricted_data field is available on quotes, applications, and policies. It is returned as a top-level field on the API response object, separate from the module field.

📘

Protected feature

Restricted data is a protected feature, and is disabled by default. To enable this feature for your organisation, please contact our support team at [email protected].

Returning restricted data from hooks

You can return restricted_data from any product module hook that produces a quote, application, or policy object. The field is optional — if you don't include it, the API response will simply not contain a restricted_data field.

Quote hook

The getQuote() function can return a restricted_data object alongside the standard quote fields. This data is saved to the quote package and carried through to subsequent steps in the policy issue flow.

const getQuote = (data) => {
  const basePremium = calculateBasePremium(data);
  const riskScore = calculateRiskScore(data);

  return new QuotePackage({
    package_name: 'comprehensive_cover',
    sum_assured: data.cover_amount,
    base_premium: basePremium,
    suggested_premium: basePremium,
    module: {
      cover_type: data.cover_type,
      cover_amount: data.cover_amount,
    },
    restricted_data: {
      risk_score: riskScore,
      underwriting_notes: 'Auto-accepted based on risk profile',
      reinsurance_premium: Math.round(basePremium * 0.35),
    },
  });
};

Application hook

The getApplication() function can also return restricted_data. Typically, you would carry over the restricted data from the quote package and add any additional sensitive application-specific information.

const getApplication = (data, policyholder, quote_package) => {
  return new Application({
    package_name: quote_package.package_name,
    sum_assured: quote_package.sum_assured,
    base_premium: quote_package.base_premium,
    monthly_premium: quote_package.suggested_premium,
    module: {
      ...quote_package.module,
      ...data,
    },
    restricted_data: {
      ...quote_package.restricted_data,
      kyc_status: 'verified',
      screening_result: 'clear',
    },
    input_data: data,
  });
};

Policy issue hook

The getPolicy() function can include restricted_data on the policy object. The restricted data will persist on the policy for its entire lifecycle and can be referenced in lifecycle hooks and scheduled functions.

const getPolicy = (application, policyholder, billing_day) => {
  return new Policy({
    package_name: application.package_name,
    sum_assured: application.sum_assured,
    base_premium: application.base_premium,
    monthly_premium: application.monthly_premium,
    start_date: moment().add(1, 'day').format(),
    end_date: moment().endOf('month').add(1, 'year').format(),
    module: {
      ...application.module,
    },
    restricted_data: {
      ...application.restricted_data,
      issued_by_system: true,
    },
  });
};

Requote hook

When a policy is requoted using the requotePolicy() function, you can update or preserve the restricted_data on the requoted policy.

const requotePolicy = (policy, requote_data) => {
  const newPremium = recalculatePremium(policy, requote_data);

  return new RequotePolicy({
    package_name: policy.package_name,
    sum_assured: policy.sum_assured,
    base_premium: newPremium,
    monthly_premium: newPremium,
    module: {
      ...policy.module,
      ...requote_data,
    },
    restricted_data: {
      ...policy.restricted_data,
      previous_premium: policy.monthly_premium,
      requote_reason: requote_data.reason,
    },
  });
};
👍

Carry restricted data through the flow

If you set restricted_data at the quote step, remember to carry it forward to the application and policy hooks. The platform does not automatically propagate restricted_data between steps — your product module code controls what is included at each stage.

How restricted data is stored

The restricted_data field is stored alongside the module column. It is not nested inside the module object. This means:

  • Restricted data does not affect or interfere with the standard module field.
  • Restricted data is persisted independently on quote_packages, applications, and policies tables.
  • Restricted data can be queried and filtered independently of module data in data exports.

Access control

Visibility of restricted_data in API responses is determined by the requestee's identity and permissions. The platform evaluates access at the HTTP layer before returning data to the caller.

Access rules by requestee type

Requestee typeAccess to restricted_dataDetails
Embed JWTNeverEmbed tokens represent end users and should never see restricted data.
Product module codeAlwaysInternal product module code (lifecycle hooks, scheduled functions) always has full access.
API keys & dashboard usersPermission-basedAccess depends on whether the requestee has the required domain-specific permission.

Required permissions

Each domain (quotes, applications, policies) has its own permission that controls access to restricted data. These permissions can be assigned to roles in the Root management dashboard.

DomainPermission
QuotesReadQuoteRestrictedModuleData
ApplicationsReadApplicationRestrictedModuleData
PoliciesReadRestrictedModuleData

When a requestee does not have the required permission, the restricted_data field is excluded from the API response entirely — it will not appear as null or an empty object.

🚧

Webhooks always include restricted data

Webhook payloads always include restricted_data regardless of the access rules described above. This is because webhooks are system-level events intended for server-to-server integrations where the data consumer is trusted.

API response examples

The examples below show how the restricted_data field appears in API responses depending on the requestee's access level.

With access

When the requestee has the required permission, the full restricted_data object is included in the response.

{
  "policy_id": "128ba0c0-3f6a-4f8b-9b40-e2066b02b59e",
  "status": "active",
  "package_name": "comprehensive_cover",
  "monthly_premium": 15000,
  "module": {
    "cover_type": "comprehensive",
    "cover_amount": 50000000
  },
  "restricted_data": {
    "risk_score": 42,
    "underwriting_notes": "Auto-accepted based on risk profile",
    "reinsurance_premium": 5250,
    "kyc_status": "verified"
  }
}

Without access

When the requestee does not have the required permission (or is using an Embed JWT), the restricted_data field is omitted from the response.

{
  "policy_id": "128ba0c0-3f6a-4f8b-9b40-e2066b02b59e",
  "status": "active",
  "package_name": "comprehensive_cover",
  "monthly_premium": 15000,
  "module": {
    "cover_type": "comprehensive",
    "cover_amount": 50000000
  }
}

Restricted data in lifecycle hooks

When your product module code runs in response to lifecycle events or scheduled functions, the restricted_data field is always available on the policy, application, and quote objects passed as arguments. This allows you to reference sensitive data in your business logic without exposing it to external consumers.

const afterPaymentSuccess = (policy, payment) => {
  const riskScore = policy.restricted_data?.risk_score;

  if (riskScore > 80) {
    return [actions.sendCustomNotification('high_risk_payment_received', policy.policy_id)];
  }

  return [];
};

Restricted data and alteration hooks

When an alteration package is applied to a policy, you can update the restricted_data on the altered policy using the applyAlteration() function.

const applyAlteration = (policy, alteration_package) => {
  const newPremium = recalculatePremium(policy, alteration_package);

  return new AlteredPolicy({
    package_name: policy.package_name,
    sum_assured: alteration_package.sum_assured || policy.sum_assured,
    base_premium: newPremium,
    monthly_premium: newPremium,
    module: {
      ...policy.module,
      ...alteration_package.module,
    },
    restricted_data: {
      ...policy.restricted_data,
      previous_premium: policy.monthly_premium,
      alteration_applied_at: new Date().toISOString(),
    },
  });
};

Best practices

  • Separate concerns — Use module for data that should be visible to all API consumers (including embedded flows), and restricted_data for sensitive information that should be limited to authorised users.
  • Carry data forward — If you set restricted data at the quote step, explicitly include it in subsequent hooks (application, policy issue). The platform does not auto-propagate between steps.
  • Don't duplicate — Avoid storing the same data in both module and restricted_data. Choose one location based on the data's sensitivity.
  • Plan for webhooks — Remember that restricted_data is always included in webhook payloads. Ensure your webhook consumers handle this data securely.

Related guides