---
title: "Catalog: products and prices"
description: "Build your catalog from product groups, products and prices, select from eight pricing models, and give an add-on a different price on each plan."
canonical_url: "https://twominutebilling.com/help/developers/concepts/catalog"
---
# Catalog: products and prices

> Build your catalog from product groups, products and prices, select from eight pricing models, and give an add-on a different price on each plan.

Each business has its own catalog, with three levels:

```text
Product group        an optional grouping for reporting and portal display
└── Product          the thing you sell — a plan, an add-on, or a one-off charge
    └── Price        one way to charge for it — a currency, an interval, a model
```

## Products

Give each product a `kind`:

<table>
<thead>
  <tr>
    <th>
      Kind
    </th>
    
    <th>
      Use it for
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        plan
      </code>
    </td>
    
    <td>
      The main thing a subscription is for. Most subscriptions carry exactly one.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        addon
      </code>
    </td>
    
    <td>
      Something bought alongside a plan, such as extra seats or premium support.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        charge
      </code>
    </td>
    
    <td>
      A one-off, rather than a recurring line.
    </td>
  </tr>
</tbody>
</table>

```bash
curl -X POST $TMBILLING_URL/v1/businesses/$BIZ/products \
  -H "Authorization: Bearer $TMBILLING_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Pro", "code": "pro", "kind": "plan" }'
```

`DELETE` archives a product by setting `active=false` rather than removing it, so your history
keeps pointing at something real.

## Prices

Give a product as many prices as you need. Each price fixes a currency, an interval and a model.
Selling one plan monthly and yearly, in USD and EUR, means four prices on one product.

```bash
curl -X POST $TMBILLING_URL/v1/businesses/$BIZ/prices \
  -H "Authorization: Bearer $TMBILLING_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "product": "pro",
    "id": "pro-monthly-usd",
    "name": "Monthly USD",
    "model": "flat",
    "currency": "USD",
    "amount_minor": 2900,
    "interval": "month"
  }'
```

Set `type` to `recurring`, the default, or `one_time`. Recurring prices need an `interval` of
`day`, `week`, `month` or `year`, plus an optional `interval_count` — so "every three months" is
`interval: "month"` with `interval_count: 3`.

### Select a pricing model

<table>
<thead>
  <tr>
    <th>
      Model
    </th>
    
    <th>
      You set
    </th>
    
    <th>
      How it charges
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        flat
      </code>
    </td>
    
    <td>
      <code>
        amount_minor
      </code>
    </td>
    
    <td>
      A fixed amount per period, whatever the quantity.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        per_unit
      </code>
    </td>
    
    <td>
      <code>
        amount_minor
      </code>
    </td>
    
    <td>
      The amount times the quantity. Use it for seats.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tiered
      </code>
    </td>
    
    <td>
      <code>
        tiers[]
      </code>
    </td>
    
    <td>
      Graduated. Each tier's units charge at that tier's rate, so a quantity spanning three tiers pays all three.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        volume
      </code>
    </td>
    
    <td>
      <code>
        tiers[]
      </code>
    </td>
    
    <td>
      Charges the whole quantity at the rate of the tier it lands in.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stairstep
      </code>
    </td>
    
    <td>
      <code>
        tiers[]
      </code>
    </td>
    
    <td>
      Charges the tier's <code>
        flat_amount_minor
      </code>
      
      , wherever in the tier the quantity falls.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        package
      </code>
    </td>
    
    <td>
      <code>
        amount_minor
      </code>
      
      , <code>
        package_size
      </code>
    </td>
    
    <td>
      Sells in blocks — 1,000 credits for $10 — rounding up to whole packages.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        usage
      </code>
    </td>
    
    <td>
      <code>
        usage_aggregation
      </code>
    </td>
    
    <td>
      Meters usage and bills in arrears for the period just closed. See <a href="https://twominutebilling.com/help/developers/recipes/usage-based-billing">
        Usage-based billing
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        percentage
      </code>
    </td>
    
    <td>
      <code>
        percentage_bps
      </code>
    </td>
    
    <td>
      Takes a cut in basis points, where <code>
        250
      </code>
      
       means 2.5%. <code>
        free_units
      </code>
      
       exempts a base amount first.
    </td>
  </tr>
</tbody>
</table>

The tier, package and percentage models bill recorded usage instead of the subscription quantity
when you give them a `usage_aggregation`. The price object then reads `metered: true`. See
[Usage-based billing](https://twominutebilling.com/help/developers/recipes/usage-based-billing).

### Reprice without repricing your customers

A `PATCH` on a price applies to new subscriptions only. Existing subscriptions keep the price you
created them with, so raising a price never silently reprices everyone on it.

To move existing customers, change their subscription items. See
[Upgrades and downgrades](https://twominutebilling.com/help/developers/recipes/plan-changes).

<note title="Set your own price ids">

`id` is a slug you set, such as `pro-monthly-usd`, unique per business. Omit it and tmbilling
derives one from the name, or from the currency, interval and model.

You cannot change it once set, and a collision returns `409`. That immutability is what lets you
use the same id in your test and live businesses.

</note>

## Add-ons

Restrict a product with `kind: "addon"` to specific plans, and give it a different price depending
on the plan it attaches to.

<endpoint method="PUT" path="/v1/businesses/{biz}/products/{prod}/applicable_plans">



</endpoint>

Restricts the add-on to a list of plans. tmbilling refuses to add it to any other plan.

<endpoint method="PUT" path="/v1/businesses/{biz}/products/{prod}/differential_prices">



</endpoint>

Sets a differential price: what the add-on costs on that particular plan, overriding its own
price. Use it to bundle an add-on cheaply with an expensive plan.

## Coupons

Coupons live in the catalog too, because they are configuration rather than transactions.

- A **coupon** is a reusable discount — percent or amount, with validity dates and constraints on
which products it applies to.
- A **coupon set** is a batch of unique single-use codes from one definition, for a campaign where
each recipient gets their own code.

See [Coupons and discounts](https://twominutebilling.com/help/developers/recipes/coupons).

## Related

- [Build your catalogue in the dashboard](https://twominutebilling.com/help/getting-started/build-your-catalogue)
- [Subscription management and pricing models](https://twominutebilling.com/product/subscriptions)
- [Coupons and discounts](https://twominutebilling.com/help/developers/recipes/coupons)


## Sitemap

See the full [sitemap](https://twominutebilling.com/sitemap.md) for all pages.
