Developer · Policies

Policy documents, straight from the quote

Endorsement steps decide which schedules, forms and clauses belong on a policy. A Mustache or Handlebars template then reads the quote response to pick the sections and fill them in, for any line of business

How it works

Price, respond, render

Swallow doesn't hold your document layouts. It hands you a response with every decision already made, so your template only has to show it

01 · Price
The model picks the paperwork
Next to the premium, endorsement steps check rules against the risk. Every rule that fires adds a form code for a schedule, endorsement or clause
02 · Respond
One response, everything in it
The quote response holds the price, the inputs, every output field and the excesses and endorsements that apply. It's all the document needs
03 · Render
Your template does the layout
A Mustache or Handlebars template turns the response into a schedule. Form codes switch sections on and off, and output fields fill them in
Endorsements

Rules that pick the forms

Each item is a rule with a form code as its value. The rules that fire land in the response's endorsements list with the step key, the rule name, the values it saw, the code and its description

Endorsement step in a fleet model
{
  "id": "policy_forms",
  "key": "policy_forms",
  "step": "endorsements",
  "name": "Policy forms",
  "def": [],
  "items": [
    {
      "id": "telematics",
      "name": "Telematics fitted",
      "exp": "{{telematics}} == true",
      "value": "END_TELEMATICS",
      "description": "Telematics data condition applies."
    },
    {
      "id": "hazardous_goods",
      "name": "Carries hazardous goods",
      "exp": "{{hazardous_goods}} == true",
      "value": "END_HAZ_GOODS",
      "description": "ADR hazardous goods extension applies."
    },
    {
      "id": "young_drivers",
      "name": "Drivers under 25",
      "exp": "{{drivers_under_25}} > 0",
      "value": "END_YOUNG_DRIVER",
      "description": "Young driver excess and named-driver condition apply."
    },
    {
      "id": "vehicle_schedule",
      "name": "More than 20 vehicles",
      "exp": "{{vehicle_count}} > 20",
      "value": "SCHED_VEHICLES",
      "description": "Vehicles listed in a separate schedule."
    }
  ]
}
Quote response, trimmed
{
  "quote_reference": "<uuid>",
  "version_reference": "<uuid>",
  "result": 17502.8,
  "valid": true,
  "input": {
    "vehicle_count": 24,
    "telematics": true,
    "hazardous_goods": false,
    "drivers_under_25": 2,
    "region": "north_west"
  },
  "output": {
    "risk_premium": 15627.5,
    "ipt": 1875.3,
    "compulsory_excess": 500,
    "result": 17502.8,
    "valid": true
  },
  "refers": [],
  "exclusions": [],
  "excesses": [
    {
      "step": "fleet_excesses",
      "name": "Young driver excess",
      "drivers_under_25": 2,
      "exp": "{{drivers_under_25}} > 0",
      "result": true,
      "value": 250
    }
  ],
  "endorsements": [
    {
      "step": "policy_forms",
      "name": "Telematics fitted",
      "telematics": true,
      "exp": "{{telematics}} == true",
      "result": true,
      "description": "Telematics data condition applies.",
      "value": "END_TELEMATICS"
    },
    {
      "step": "policy_forms",
      "name": "Drivers under 25",
      "drivers_under_25": 2,
      "exp": "{{drivers_under_25}} > 0",
      "result": true,
      "description": "Young driver excess and named-driver condition apply.",
      "value": "END_YOUNG_DRIVER"
    },
    {
      "step": "policy_forms",
      "name": "More than 20 vehicles",
      "vehicle_count": 24,
      "exp": "{{vehicle_count}} > 20",
      "result": true,
      "description": "Vehicles listed in a separate schedule.",
      "value": "SCHED_VEHICLES"
    }
  ]
}

Three of the four rules fired. The fleet carries no hazardous goods, so END_HAZ_GOODS is missing and its section won't print. The API page has the full response

Templates

The response is the template data

Turn the form codes into flags, then hand the whole response to your template. Flags show or hide sections, partials hold each form's wording and output fields fill in the figures

Render the quote
import Mustache from "mustache";

// The response from POST /sw/quotes/:project_reference,
// or a saved quote from GET /sw/quotes/:quote_reference
const quote = await res.json();

// Turn the form codes into flags the template can test
const forms = Object.fromEntries(
  quote.endorsements
    .filter((e) => e.step === "policy_forms")
    .map((e) => [e.value, true])
);

const html = Mustache.render(schedule, { ...quote, forms }, partials);
schedule.mustache
<h1>Fleet policy schedule</h1>
<p>Policy {{quote_reference}} · {{input.vehicle_count}} vehicles</p>

<table>
  <tr><td>Premium</td><td>{{output.risk_premium}}</td></tr>
  <tr><td>Insurance Premium Tax</td><td>{{output.ipt}}</td></tr>
  <tr><td>Total payable</td><td>{{result}}</td></tr>
  <tr><td>Compulsory excess</td><td>{{output.compulsory_excess}}</td></tr>
</table>

{{#forms.SCHED_VEHICLES}}{{> vehicle_schedule}}{{/forms.SCHED_VEHICLES}}
{{#forms.END_TELEMATICS}}{{> telematics_condition}}{{/forms.END_TELEMATICS}}
{{#forms.END_HAZ_GOODS}}{{> hazardous_goods}}{{/forms.END_HAZ_GOODS}}
{{#forms.END_YOUNG_DRIVER}}{{> young_driver}}{{/forms.END_YOUNG_DRIVER}}

<h2>Endorsements</h2>
{{#endorsements}}
  <p><strong>{{value}}</strong> {{name}}. {{description}}</p>
{{/endorsements}}

The same syntax works in Mustache and Handlebars. Liquid, Jinja or a PDF generator that takes JSON work the same way

Rendered schedule
Fleet policy schedule
Policy 7f3c…e21a · 24 vehicles
Premium
15,627.50
Insurance Premium Tax
1,875.30
Total payable
17,502.80
Compulsory excess
500
SCHED_VEHICLESSchedule of vehicles · 24 vehicles
END_TELEMATICSTelematics condition
END_HAZ_GOODSHazardous goods extension · not included
END_YOUNG_DRIVERYoung driver condition
Other steps

Every step feeds the document

Endorsements choose the sections. The rest of the model supplies what goes in them, and whether a document should go out at all

StepDocument partHow it's used
endorsementsForms, schedules and clausesEach rule that fires adds a code, and the template uses the codes to choose which sections and wordings appear
excessesExcess scheduleRules that add excesses for a risk, such as young drivers or high-value items. Total them into an output field to print the figure
exclusion · referWhether to issue at allA declined quote comes back with valid false, so no document goes out. A referred one can wait for an underwriter first
calculationPremium breakdownNet premium, tax, fees, commission and instalments, each as an output field and printed to the penny
collectionWording lookupsTables of territory clauses, insurer details, regulatory text or limits by product, looked up per risk instead of hard-coded in the template
transformDisplay valuesLabels, dates and text shaped for the document, such as a policy period or a cover description
Why do it this way

Documents that match the price

The rules about cover live in one place, get tested with the rating and go live as one version

Accurate
Priced and worded by the same rules
The version that priced the quote also chose its forms, so a document can't carry a clause the rating didn't allow for, or miss one it did
Any line
Your forms, your codes
Fleet, property, pet, cyber or a US filed programme. Form codes are whatever your wordings library already uses, and one model can have several endorsement steps
Traceable
Every section has a reason
Each endorsement in the response names the rule, the expression and the values that fired it
Repeatable
Render it again any time
Quotes are saved with their version. Fetch one and render it again for a reissue, a mid-term change or an audit