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

# Catalog: products, plans and prices

> Describe what you sell once, then subscribe customers to it

Your catalog has four layers. Most products only use the first three; charges matter when one plan bills several things.

| Layer | Example | What it holds |
| - | - | - |
| **Product** | "Inkwell" | Name, description, tax category. |
| **Plan** | "Pro", "Team" | The tier a customer picks. A product holds several. |
| **Charge** | "Platform fee", "Seats", "SMS" | One thing the plan bills. Every plan starts with one. |
| **Price** | ₦5,000 a month, \$5 a month | What a charge costs in one currency and billing interval. |

## The quick way: a product with one price

For a simple plan, create the product with its price in one call. Bouclay creates a plan alongside it:

```bash theme={null}
curl https://bouclay.com/api/v1/products \
  -H "Authorization: Bearer $BOUCLAY_KEY" \
  -H "Idempotency-Key: catalog-pro-v1" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Pro",
    "price": {
      "type": "recurring",
      "pricingModel": "standard",
      "unitAmount": 5000,
      "currency": "NGN",
      "billingInterval": "month"
    }
  }'
```

The response lists the new `priceIds` and the plan.

## Several plans on one product

Create the product, then each plan, then each plan's prices:

```bash theme={null}
# A plan
curl https://bouclay.com/api/v1/products/prod_…/plans \
  -H "Authorization: Bearer $BOUCLAY_KEY" \
  -H "Idempotency-Key: plan-team-v1" \
  -H "Content-Type: application/json" \
  -d '{"name": "Team", "code": "team"}'

# Its monthly and yearly prices
curl https://bouclay.com/api/v1/products/prod_…/prices \
  -H "Authorization: Bearer $BOUCLAY_KEY" \
  -H "Idempotency-Key: price-team-monthly-v1" \
  -H "Content-Type: application/json" \
  -d '{
    "planId": "plan_…",
    "type": "recurring",
    "pricingModel": "standard",
    "unitAmount": 15000,
    "currency": "NGN",
    "billingInterval": "month"
  }'
```

A plan can offer several intervals and currencies: add one price for each. When you subscribe a customer you can name the plan and Bouclay picks the price that matches.

## Prices never change once used

A price that a subscription or invoice has used is frozen: its amount, currency and interval can't be edited. This protects every invoice already issued. To change what you charge, create a new price and archive the old one. Existing subscribers stay on the old price until you move them. See [Changing subscriptions](https://docs.bouclay.com/billing/changing-subscriptions.md).

## Billing intervals

`billingInterval` is `day`, `week`, `month` or `year`, and `billingFrequency` multiplies it: `month` with `3` bills every three months.

## Pricing models

| Model | How it charges | Example |
| - | - | - |
| `standard` | The same amount per unit. | ₦5,000 per seat |
| `graduated` | Each slice of the quantity at its own tier's rate. | First 10 seats ₦5,000, the rest ₦4,000 each |
| `volume` | Every unit at the rate of the tier the quantity lands in. | 25 seats, all at ₦4,000 |
| `package` | Per block of units, rounding up. | ₦1,000 per 100 SMS |

> [!NOTE]
>
> `graduated`, `volume` and `package` are early access.

Tiers are a list of `upTo` (the last one `null`) and `unitAmount`, with an optional `flatAmount` for the tier. Included units are a first tier at 0:

```json theme={null}
"tiers": [
  { "upTo": 10000, "unitAmount": 0 },
  { "upTo": null, "unitAmount": 3 }
]
```

## Seats and quantity limits

A `licensed` price charges for a quantity you set, such as seats. `minQuantity` and `maxQuantity` set a range ("Team: 5 seats or more"); anything outside it is refused. Name the unit on the charge with `unitLabel` and `unitLabelPlural` (`PATCH /charges/{id}`), and invoices read "Team · 3 learners" instead of a bare number.

## Free trials

Add `trialLength` and `trialUnit` (`day`, `week` or `month`) to a recurring price.

* `trialRequiresPaymentInfo: true` starts the trial only once the customer saves a card. Without it, the trial starts at once with no card.
* `trialOncePerCustomer: true` gives each customer one trial per plan.

What happens when a trial ends without a card is set per subscription. See [Subscriptions](https://docs.bouclay.com/billing/subscriptions.md#trials).

## Setup and joining fees

`setupFee` on a recurring price is billed once, on a new subscription's first paid invoice: at once, or when a trial ends. Name the line with `setupFeeName` (such as "Joining fee"). It is never billed on a renewal, a plan change or a resume.

`setupFeeAgainAfterMonths` lets a returning customer back without paying it again, unless that many months have passed since their last subscription to the product ended.

## Plans with several charges

A plan can bill several things together, such as a platform fee plus two kinds of usage. Each is a charge with its own prices, and the invoice shows one line per charge. See [Sell several kinds of usage in one plan](https://docs.bouclay.com/usage-plans.md).

## Taxes on products

Each product has a tax category: `standard`, `zero_rated` or `exempt`. A charge can override it. See [Invoices](https://docs.bouclay.com/billing/invoices.md#vat).

## Archiving

Archive products, plans, prices and charges you no longer sell. Archived items can't start new subscriptions; existing subscriptions keep billing. To move everyone off a plan on a date, see [Retire a plan](https://docs.bouclay.com/billing/changing-subscriptions.md#retire-a-plan).


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