# Catalog

> Products, prices and features per brand. Every product has a tax category, prices are exact and immutable once used, and lookup keys let your code refer to "the current price" without hard-coding IDs.

Each brand has its own **catalog**: the products it sells, their prices, and the features those products grant. Test and live mode have separate catalogs, so you can build and try a catalog in test mode before you create the live one.

## Products

A **product** is something a brand sells. Every product has:

- a **name** and an optional description, shown at checkout, on invoices and on the pricing table;
- a **tax category**, such as `saas`, `ebook` or `electronically_supplied_service`. It's required, because it decides which tax rate applies in each jurisdiction. Most digital products use `electronically_supplied_service`;
- a **kind**: `plan` products appear on the brand's pricing table, `standard` ones don't;
- **entitlements**: values of the brand's features that the product grants, such as `seats.max = 10`;
- optional pricing-table hints (a display order and a highlight), image URLs and metadata.

Products are never deleted. **Archiving** a product takes it off the pricing table and stops new sales and new prices; existing subscriptions and invoices are unaffected, and you can reactivate it later. Changing a product's tax category applies to future sales; issued invoices keep the category they were issued with.

## Prices

A product has one or more **prices**. Every price has a currency and a **tax behavior**: `inclusive` means the amount includes tax (required for consumer prices in the EU and the UK, where the customer pays the same gross price in every country), `exclusive` means tax is added on top. There are three types:

| Type          | What it charges                                                                                                                                                                                                            |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `one_time`    | A fixed amount, or a **custom amount** the customer chooses between a minimum and a maximum (with an optional suggestion).                                                                                                 |
| `credit_pack` | Like a one-time price, and the customer receives prepaid credits in their wallet. Optional **bonus credits** are granted as promotional credits, and credits can expire after a number of days.                            |
| `recurring`   | A subscription price, billed every day, week, month or year (or every few of them). **Per unit**: a unit price times the quantity, such as seats. **Tiered**: priced by tiers. **Metered**: the quantity comes from usage. |

**Tiered prices** come in two modes. With **graduated** tiers, each tier prices the units that fall in it: with €5 for seats 1–10 and €4 from seat 11, 15 seats cost 10 × €5 + 5 × €4 = €70. With **volume** tiers, the whole quantity is priced at the tier it reaches: 15 seats cost 15 × €4 = €60. Each tier can add a **flat amount**, once, when the quantity reaches it. Zero units cost nothing.

Any recurring price can carry **trial days** and **included credits**, granted into the customer's wallet each period. Recurring prices are sold with subscriptions.

A recurring price is **licensed** (the quantity is seats or units) or **metered** (`usageType: metered`): its quantity comes from the usage events your integration records for a **meter**, such as `api.calls`, and it's billed at the end of each period. A metered price charges per unit of usage (a unit price or tiers) or a percentage of an amount, such as 0.4% of processed volume, with an optional free allowance and a minimum and maximum per period. See [Usage billing](https://ownrecord.io/docs/concepts/usage.md).

### Amounts

Amounts are exact: integers in micros (millionths of the major unit), sent as strings, so 12.50 EUR is `{ "amount": "12500000", "currency": "EUR" }`. Amounts that are charged as they are use whole minor units of the currency (cents for EUR, whole yen for JPY). **Unit prices** can be finer, such as €0.0025 per unit, because the total is rounded once, on the invoice line. A tier's unit price is `unitAmountMicros`.

A credit pack's credits equal its amount. What the wallet receives follows the brand's **credit basis**: the net amount by default, or the gross amount, so with a tax-inclusive price under the net basis the wallet receives the amount without tax. Anything extra is given as bonus credits.

### Immutable once used

Once a checkout session or payment uses a price, what it charges can't change: its amounts, currency, tax behavior, type and tiers stay as they were, so every invoice and receipt keeps matching the price. A price's view says so with `used: true`. Its nickname, lookup key, metadata and active state can still change. Until it's used, you can correct its terms.

To change what a used price charges, create a new price, move the lookup key to it, and archive the old one.

### Lookup keys

A **lookup key** is a stable name for a price, such as `pro_monthly`, unique in the brand and mode. Your code asks for "the price with lookup key `pro_monthly`" instead of storing a price ID. Moving a key to another price is always explicit (**Move lookup key**): the key leaves the old price and lands on the new one in a single step, so nothing ever sees two prices with the same key. Setting a key that another price already has is refused.

## Features

**Features** are the capabilities and limits your products grant, defined per brand: a `boolean` (on or off, such as `feature.export`), a `limit` (a whole number, such as `seats.max`) or an `enum` (one of a set of values, such as `support.level`). Each feature has a default value and a rule for combining values when a customer has several products. A feature's key and type never change, and enum values can be added but not removed, so values your products already grant stay valid. See [Entitlements](https://ownrecord.io/docs/concepts/entitlements.md) for how customers' entitlements are worked out.

## The pricing table

The brand's **pricing table** lists its active plans in display order, with their active prices and the features each plan grants by name. It's a public JSON endpoint you can call from your own site without an API key, rate-limited per IP address: `GET /api/v1/public/{brandSlug}/pricing_table`. It contains only what customers may see: no nicknames, metadata or archived prices.

## The catalog in the console

Open a brand and choose **Catalog**. Owners and admins can change it, and developers can in test mode; finance, tax managers, support and viewers can see it.

- **Products**: every product, filtered by status and kind. A product's page shows its details and entitlements, and its prices with their terms, lookup keys and status. From there you add prices, move a lookup key onto a price, and archive prices or the product.
- **Features**: the brand's features; create one or edit its name, combine rule, default value and enum values.
- **Pricing table**: a preview of what the public endpoint returns, and how to fetch it.

Every page names the operation behind it, so you can do the same through the API or an MCP client.
