Model building

Build and test a real pricing model

Ptally models are plain, declarative JSON. AI agents can write and validate them, and actuaries, underwriters and auditors can read them line by line. Below is a complete UK pet insurance model. Every number on this page was produced by the Ptally rating engine.

Model format

AI friendly. Human readable.

The agent that drafts a model, the engine that prices it and the person who approves it all work from the same file. There is no generated code to review.

For AI
Built for agents to write
A strict JSON schema, a small set of step types and server-side validation let an AI client draft a model, check it and run its tests over MCP.
For people
Reads like a rating guide
Inputs have names and plain-English descriptions, rate tables are stored as rows and formulas are written out in full. An underwriter can review a change without reading code.
For both
Tested and versioned
Every version has its own test suite. Whether a person or an agent made a change, you can check it against known prices before it goes live.
The model

UK Pet Insurance: Cats and Dogs

Rates annual lifetime vet-fee cover for one cat or dog in the UK. Base rates by species and annual vet-fee limit are adjusted for age band, breed, region, voluntary excess and neutering, then 12% Insurance Premium Tax is added. Older pets and breed/species mismatches are declined; high-limit cover on high-risk breeds is referred.

7inputs
6rating tables
4calculations
5/5tests passing
01Inputs
Define what you rate on
Every input has a key, a type, a default and a plain-English description for brokers. Fields with a fixed list of options carry that list with them.
{
  "input": {
    "format": {
      "species": {
        "key": "species",
        "label": "Species",
        "type": "string",
        "def": "cat",
        "items": ["cat", "dog"]
      },
      "breed": {
        "key": "breed",
        "label": "Breed",
        "type": "string",
        "def": "moggie",
        "items": ["moggie", "persian", "bengal", "maine_coon", "mixed_dog", "labrador", "cockapoo", "german_shepherd", "french_bulldog"]
      },
      "pet_age": {
        "key": "pet_age",
        "label": "Pet age (years)",
        "type": "integer",
        "def": 3
      },
      "vet_fee_limit": {
        "key": "vet_fee_limit",
        "label": "Annual vet-fee limit (£)",
        "type": "integer",
        "def": 4000,
        "items": [1000, 2000, 4000, 7000]
      }
    }
  }
}
02Collections
Load your rating tables
Rate tables are plain rows. Lookups chain filters, so a species-specific age band is one line. Upload or download any table as CSV from the dashboard.
{
  "id": "age_bands",
  "step": "collection",
  "retain": true,
  "collection": [
    ["species", "age_min", "age_max", "age_factor"],
    ["cat", 0, 1, 0.9],
    ["cat", 2, 4, 1],
    ["cat", 5, 7, 1.25],
    ["cat", 8, 10, 1.6],
    ["cat", 11, 20, 2.1],
    ["dog", 0, 1, 0.95],
    ["dog", 2, 4, 1],
    ["dog", 5, 7, 1.35],
    ["dog", 8, 10, 1.85],
    ["dog", 11, 20, 2.4]
  ],
  "format": {
    "pet_age_factor": {
      "key": "pet_age_factor",
      "label": "Age factor",
      "exp": "collection.filter(species=={{species}}).filter(age_min<={{pet_age}}).filter(age_max>={{pet_age}}).map(age_factor).first()",
      "type": "decimal",
      "def": 1
    }
  }
}
03Calculations
Build the premium
Calculation steps run in order and can reference any input or earlier output. Here the risk premium multiplies six factors, then 12% UK Insurance Premium Tax is added.
[
  {
    "id": "risk_premium",
    "step": "calculation",
    "formula": "round({{base_premium}} * {{pet_age_factor}} * {{breed_factor}} * {{region_factor}} * {{excess_factor}} * {{neuter_factor}}, 2)"
  },
  {
    "id": "ipt",
    "step": "calculation",
    "formula": "round({{risk_premium}} * 0.12, 2)"
  },
  {
    "id": "total_premium",
    "step": "calculation",
    "formula": "round({{risk_premium}} + {{ipt}}, 2)"
  }
]
04Underwriting
Add decline and refer rules
Exclusions make a quote invalid; refers flag it for an underwriter while still returning a price. Pets over 14 and breed/species mismatches decline; £7,000 cover on high-risk breeds refers.
[
  {
    "id": "pet_exclusions",
    "step": "exclusion",
    "items": [
      {
        "id": "too_old",
        "name": "Pet older than 14 at inception",
        "exp": "{{pet_age}} > 14",
        "def": false
      },
      {
        "id": "breed_mismatch",
        "name": "Breed does not match species",
        "exp": "{{breed_matches}} == 0",
        "def": false
      }
    ]
  },
  {
    "id": "pet_refers",
    "step": "refer",
    "items": [
      {
        "id": "high_risk_high_limit",
        "name": "£7,000 limit on a high-risk breed",
        "exp": "{{vet_fee_limit}} == 7000 && ({{breed}} == 'french_bulldog' || {{breed}} == 'persian')",
        "def": false
      }
    ]
  }
]
05Tests
Add tests
Tests live inside the model. Each one pins inputs to an expected result, so you can rerun the whole suite on every change before you publish.
{
  "tests": [
    {
      "id": "persian_london",
      "key": "persian_london",
      "name": "Persian cat, 3, Greater London, £4k limit, £100 excess, neutered",
      "input": {
        "species": "cat",
        "breed": "persian",
        "pet_age": 3,
        "region": "greater_london",
        "vet_fee_limit": 4000,
        "excess": 100,
        "neutered": true
      },
      "output": {
        "result": 255.34,
        "valid": true
      }
    }
  ]
}
Test results

Five scenarios, all passing

Two priced quotes checked against a hand calculation to the penny, two declines and a refer. This is the engine's output for the suite above.

ScenarioAnnualMonthlyValidRefer
Persian cat, 3, Greater London, £4k limit, £100 excess, neutered£255.34£21.28VALID-
Labrador, 6, North, £2k limit, £250 excess, entire£269.76£22.48VALID-
Decline: cat aged 15--DECLINED-
Refer: French Bulldog on £7k limit£621.47£51.79VALIDREFER
Decline: cat with a dog breed--DECLINED-
06 · Release

Draft, approve, publish

Every version goes through draft, approval and publishing, so changes are reviewed before they price live quotes.

Draft
Build and edit
Every change starts as a draft version, copied from the live model. Only drafts can be edited, either by a person in the editor or by an AI client over MCP.
Approved
Review
Run the tests, check the changes and approve. Approved versions are live: a quote without a pinned version uses the latest approved or published one.
Published
Release
Publish to mark the version as released; unpublish and republish whenever you need to. Every status change is recorded with who made it and when.
Build it from your AI client

Connect Claude, ChatGPT or Cursor to the Ptally MCP server. The agent can read the rules, start from an example model, validate it, edit drafts and run the test suite.

Connect over MCP
Want us to build it?

Send us your rating guide, spreadsheet or filing. Our consultants build, test and launch the model with no implementation fee.

Talk to our team