# Price setup

A rental's own system, or its own AI agent, can read and change the rental's prices: the rates, the rate periods and the price rules. Reads need `price.read`. Writes need `price.write` and a key for the whole organization. A channel's key never writes a price.

A channel's key reads only what its channel sells at. It reads its own price list alone: another list answers `404`. It reads the price rules that move its prices: a rule that names its channel, and a rule that names no channel, unless that rule names only other price lists. It never reads a rule that names only other channels. A key for the whole organization reads every list and every rule.

## How prices are built

- A **price list** holds the price the customer pays. A rental has a default list, and may have more, such as one for a reseller. A **derived list** takes each rate from its parent, moved by a percentage or an amount for each day, then rounded.
- A **rate period** is a stretch of the year. Recurring rate periods split the year with no gap and no overlap. A one-off rate period, such as Easter 2027, wins for its days in that year. Rate periods belong to the rental, so every list prices the same ones.
- A **rate** is the daily price of a car type in a rate period on a list. It has **steps**: the length of the whole booking picks one step, and that step prices every day. A cell is one car type in one rate period, with its steps.
- A **price rule** moves a price by itself. It names its car types, a window (rate periods or one date range), a condition, and its lists. On the car, a rule with a window moves only the booking's days inside it.

## Preview first

Every write takes `?preview=true`. The call runs every check and answers what it would change, and writes nothing. Show that answer to a person before the write.

```
PUT /rentals/{slug}/price-lists/{id}/rates?preview=true
```

Every write goes into the rental's price history, under the key's name, and the rental's webhooks hear [`price.changed`](/webhooks).

## Rates

`GET /rentals/{slug}/price-lists/{id}/rates` gives every cell the list charges. A cell with `own: false` comes from the parent.

`PUT /rentals/{slug}/price-lists/{id}/rates` writes up to 500 cells, all or none:

```json
{
  "cells": [
    {
      "carTypeId": "…",
      "ratePeriodId": "…",
      "steps": [
        { "fromDays": 1, "dailyMinor": 21000 },
        { "fromDays": 7, "dailyMinor": 18000 }
      ]
    }
  ]
}
```

Only the steps sent change, as on the Rates page. Each is laid over the steps the cell holds now, or takes from the parent list or the base rate, and a step not sent keeps its price. A `dailyMinor` of `null` takes that step out. An empty `steps` removes the cell's own steps, so a derived list follows its parent for that cell again. A cell left out stays as it is.

A new step starts on day 2 to 30, else 422 `rate_step_day`. A first day the list holds already keeps its day. A cell left with no price from day 1 answers 422 `rate_steps`.

## Rate periods

`POST /rentals/{slug}/rate-periods` adds one, `PATCH /rentals/{slug}/rate-periods/{id}` changes one, and `DELETE /rentals/{slug}/rate-periods/{id}?giveTo=…` removes one. A recurring rate period takes its days from its neighbours and may trim them, never take one whole. A removal takes the period's rates, and the price rules that named only it. The answer lists both in `removed` and `removedPriceRules`.

## Price rules

`GET`, `POST /rentals/{slug}/price-rules`, `PATCH` and `DELETE /rentals/{slug}/price-rules/{id}`, and `PUT /rentals/{slug}/price-rules/order`.

```json
{
  "name": "July surcharge",
  "percentBp": 1000,
  "appliesTo": "car",
  "carTypeIds": ["…", "…"],
  "dateFrom": "2027-07-01",
  "dateTo": "2027-07-14"
}
```

`percentBp` 1000 is +10%. Or give `amountMinor` with `amountPer`: `day` adds it on each day inside the window, `booking` once. A rule on the whole booking (`appliesTo: "booking"`) moves the total once, when a day falls inside its window. A new rule goes last, so it compounds on the rules before it. Give `position` to put it elsewhere.
