> ## Documentation Index
> Fetch the complete documentation index at: https://docs.effectiveai.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Write your first rater

> Write a small dwelling fire rater in RSL, check it with rslc, package it as a bundle, and price a policy at 1,028.

[Install Relativities and download the synthetic examples](/relativities/setup) before running this page.

In this tutorial, you turn one page of an invented rating manual into a working rater. You check
it, package it, and price a policy in Python. Each step shows the output you get.

```mermaid theme={null}
flowchart LR
  folder["1. Make a folder"] --> header["2. Name the filing"]
  header --> data["3. Add the filed numbers"]
  data --> csv["4. Write the rate table"]
  csv --> records["5. Describe the policy and the math"]
  records --> check["6. Check"]
  check --> export["7. Package"]
  export --> rate["8. Price a policy"]
```

The manual prices a dwelling fire policy. It reads a rate per peril from a table, by the
dwelling's protection class, and applies it per 1,000 of Coverage A. It adds 1.5% of each
scheduled item's value and a 35 policy fee. The total rounds to whole dollars.

You need the `rslc` command on your path, and Python with the `rslc` package.

## 1. Make a folder

A rater is a folder. It holds the source file `rater.rsl` and one CSV file per table:

```bash theme={null}
mkdir -p dwelling/tables
cd dwelling
```

## 2. Name the filing

Open `rater.rsl` and write the two header blocks:

```rsl theme={null}
spec {
    rsl: "0.4",
    carrier: "Example Mutual",
    state: AZ,
    product: "Dwelling Fire (tutorial)",
    effective_date: 2026-01-01
}

Sources = {
    manual: Source = {
        artifact: artifact("00000000-0000-4000-8000-000000000000"),
        kind: pdf
    }
}
```

* `spec` names the carrier, state, product and effective date of the filing.
* `rsl: "0.4"` is the language version line. Copy it as it is.
* `Sources` names the filed documents. Here `manual` is the rating manual, a PDF. A citation such as `manual:p3` points to its page 3.

## 3. Add the filed numbers

Add the names the manual uses, its one fixed number and its rate table:

```rsl theme={null}
Types = {
    Peril = {
        fire,
        wind,
        theft
    },
    ProtectionClass = IntBands(1, 4, 7, 10..),
    Money = Num {precision: 0.01}
}

Constants = {
    expense_fee [cite: manual:p9] = 35
}

Tables = {
    BaseRate [cite: manual:p3] :
        table dims (protection: ProtectionClass) -> {
            rate: Rate[Peril]
        }
        columns: {
            rate[fire]: "fire",
            rate[wind]: "wind",
            rate[theft]: "theft"
        }
}
```

* `Peril` lists the three perils the manual rates.
* `ProtectionClass` groups protection classes into bands: 1 to 3, 4 to 6, 7 to 9, and 10 and up.
* `Money` is an amount in whole cents.
* `expense_fee` is the 35 policy fee from page 9.
* `BaseRate` is the table on page 3: one rate per peril for each band.

## 4. Write the rate table

Write the table's rows in `tables/BaseRate.csv`:

```csv theme={null}
protection,fire,wind,theft
1,2.10,0.80,0.40
4,2.60,0.80,0.45
7,3.40,0.85,0.50
10,5.20,0.90,0.60
```

Each `protection` cell names a band by its first class. Check the rater so far:

```bash theme={null}
rslc check rater.rsl
```

```text theme={null}
FAIL rater.rsl (strict)
error: E-RATING-OUTCOME @0-0: strict rater check requires a non-empty top-level `RatingOutcomes` block
```

A rater must say which value is its premium. The next step adds it.

## 5. Describe the policy and the math

Add the records, which describe the policy, and the outcome, which names the premium:

```rsl theme={null}
Records = {
    Policy = {
        inputs {
            dwelling: Dwelling,
            items: List(Item)
                where len(self) in [0, 10] [code: "TOO-MANY-ITEMS", message: "A policy schedules at most ten items.", cite: manual:p6]
        },
        step premium {
            peril_premium[p: Peril]: Premium [cite: manual:p8] =
                round(self.dwelling.coverage_a / 1000 * lookup!(BaseRate, self.dwelling.protection_class).rate[p], 2, half_up),
            item_charge: Premium [cite: manual:p6] = sum(self.items as i, i.charge),
            premium: Premium [cite: manual:p9] =
                round(sum(Peril as p, self.peril_premium[p]) + self.item_charge + expense_fee, 0, half_up)
        }
    },
    Dwelling = {
        inputs {
            coverage_a: Money where self in [50000, 2000000],
            protection_class: Num {precision: int}
                where self in [1, 10] [code: "PROTECTION-CLASS", message: "Protection class must be 1 to 10.", cite: manual:p4]
        }
    },
    Item = {
        inputs {
            value: Money where self in [0.01, 50000]
        },
        step item {
            charge: Premium [cite: manual:p6] = round(self.value * 0.015, 2, half_up)
        }
    }
}

RatingOutcomes = {
    premium: Policy.premium
}
```

* A policy has one dwelling and up to ten scheduled items. These are the **inputs**: the facts a request sends.
* Each `where` line is a **rule** on an input. A rule with a `code` is a rule from the manual.
* `peril_premium[p: Peril]` holds one premium per peril.
* `lookup!` reads the rate table.
* `round(x, 0, half_up)` rounds to whole dollars, half up.
* `RatingOutcomes` publishes `Policy.premium` as the rater's premium.

[RSL at a glance](/relativities/rsl/language-tour) explains each of these forms.

## 6. Check

Check the rater:

```bash theme={null}
rslc check rater.rsl
```

```text theme={null}
PASS rater.rsl (strict)
cost premium: 12 + 2*|Policy.items| (each list of size 1: 14, 3: 18, 10: 32)
```

`PASS` means each request that passes the rules gets a premium. The cost line counts the steps of
work for one premium: 12, plus 2 per item.

Check the layout too:

```bash theme={null}
rslc fmt --check rater.rsl
```

```text theme={null}
PASS rater.rsl is formatted
```

## 7. Package

Export the rater as a **bundle**, a zip file that holds everything needed to rate. Unpack it:

```bash theme={null}
rslc export rater.rsl --format bundle --out dwelling.zip
unzip dwelling.zip -d bundle
```

```text theme={null}
WROTE bundle: dwelling.zip
Archive:  dwelling.zip
  inflating: bundle/rater-manifest.json
  inflating: bundle/rater.py
  inflating: bundle/rater.rsl
  inflating: bundle/tables/BaseRate.csv
```

## 8. Price a policy

Price a 250,000 dwelling in protection class 5 with two scheduled items.
Load the `bundle` folder you unpacked in the preceding step, from the same `dwelling` working directory:

```python theme={null}
import json

import rslc

rater = rslc.PreparedRater("bundle")
policy = {
    "Policy": {
        "dwelling": {"coverage_a": 250000, "protection_class": 5},
        "items": [{"value": 1200}, {"value": 800}],
    }
}
print(rater.rate(json.dumps(policy)))
# {"ok":true,"outputs":{"Policy":{"dwelling":{},"items":[{},{}],"premium":1028}}}
```

The premium is 1,028. To see each value behind it, rate with `detailed=True`:

```python theme={null}
print(rater.rate(json.dumps(policy), detailed=True))
# {"ok":true,"outputs":{"Policy":{"dwelling":{},"items":[{"charge":18},{"charge":12}],"item_charge":30,"premium":1028,"peril_premium":{"fire":650,"wind":200,"theft":112.5}}}}
```

| Value | Calculation | Result |
| - | - | - |
| Fire | 250 × 2.60 | 650 |
| Wind | 250 × 0.80 | 200 |
| Theft | 250 × 0.45 | 112.50 |
| Items | 1,200 × 0.015 + 800 × 0.015 | 30 |
| Fee | `expense_fee` | 35 |
| Premium | 1,027.50, rounded half up | 1,028 |

Now send a protection class of 11:

```python theme={null}
policy = {"Policy": {"dwelling": {"coverage_a": 250000, "protection_class": 11}}}
print(rater.rate(json.dumps(policy)))
# {"ok":false,"refusal":{"error":"BusinessRejection","truncated":false,"violations":[{"cite":"manual:p4","code":"PROTECTION-CLASS","constraint":"protection_class: where self in [1, 10]","message":"Protection class must be 1 to 10.","path":"Policy.dwelling.protection_class","value":11}]}}
```

The rater refuses the request before it calculates anything. The refusal names the rule, its
message and its manual page.

## Next steps

* [RSL at a glance](/relativities/rsl/language-tour) shows every part of the language.
* [What rslc checks and guarantees](/relativities/rsl/explanation/checks-and-guarantees) explains `PASS` and refusals.
* [Use the rslc commands](/relativities/rsl/commands) covers each command in depth.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.