# Plans and rate cards

> Change plans and rate cards, including dimensional rate cards.

Source: https://metrocli.com/docs/guides/plans-and-rate-cards/ · Docs index: https://metrocli.com/llms.txt

## Goal

Create the `Acme Standard` rate card with a $49 monthly seat price and a 2-cent usage price, then add a rate to it later. Prices are in cents. See [Rate cards](/docs/concepts/rate-cards).

## Preview

Every rate needs `product_id`, `starting_at`, `entitled` and `rate_type`. Each `starting_at` must fall on a UTC hour. A `SUBSCRIPTION` rate needs a `billing_frequency` that matches the subscriptions that will use it.

```yaml
# ratecard.yaml
name: Acme Standard
aliases:
  - name: acme_standard
rates:
  - product_id: 00000000-0000-4000-8000-000000000010   # SUBSCRIPTION product
    starting_at: 2026-12-01T00:00:00.000Z
    entitled: true
    rate_type: SUBSCRIPTION
    price: 4900
    billing_frequency: MONTHLY
  - product_id: 00000000-0000-4000-8000-000000000011   # USAGE product
    starting_at: 2026-12-01T00:00:00.000Z
    entitled: true
    rate_type: FLAT
    price: 2
```

```bash
metro ratecard create preflight --file=ratecard.yaml --sandbox --out=plans/ratecard.json
```

```text
metro ratecard create preflight (sandbox, key sha256:80f0714120b8) - read-only, nothing written to Metronome.
  rate card: null -> {"name":"Acme Standard","aliases":[{"name":"acme_standard"}],"rates":"2 rates"}
Steps: POST /v1/contract-pricing/rate-cards/create ; POST /v1/contract-pricing/rate-cards/addRates
Payload hash: sha256:98ebc15e78183261ab2d2405a404baeb4648ff08a9178473dfe05543f182d868
Wrote plan: ./plans/ratecard.json
```

What preflight checks:

- **The name is the anchor.** Rate-card create has no uniqueness key, so metro refuses a name that already exists, at preflight and again just before writing.
- **Aliases stay put.** In Metronome a new card silently takes an alias from the card that holds it. metro refuses that unless you pass `--allow-alias-move`.
- **Every product.** Each must exist and not be archived. A `SUBSCRIPTION` rate must point at a `SUBSCRIPTION` product.
- **`addRates` only**, at most 100 rates per call.

## Confirm

Read the steps and the hash, then dry-run. The dry run shows `"rate_card_id":"$step:create.data.id"`: metro fills that in with the ID the create step returns.

```bash
metro ratecard create apply --file plans/ratecard.json --sandbox \
  --confirm-hash sha256:98ebc15e78183261ab2d2405a404baeb4648ff08a9178473dfe05543f182d868
```

## Apply

```bash
metro ratecard create apply --file plans/ratecard.json --sandbox \
  --confirm-hash sha256:98ebc15e78183261ab2d2405a404baeb4648ff08a9178473dfe05543f182d868 \
  --apply --confirm-live-apply=I_UNDERSTAND_THIS_WILL_WRITE_LIVE_METRONOME_SANDBOX
```

### Add rates to an existing card

Rates are append-only. To change a price, add a rate from a later `starting_at`:

```bash
metro ratecard add-rates preflight --rate-card-id=00000000-0000-4000-8000-000000000103 \
  --file=rates.yaml --sandbox --out=plans/rates.json
```

It refuses any rate that already exists. Apply it the same way.

### Dimensional rate cards

To price one usage product by a dimension, such as an action code, start from a plan and check it offline:

```bash
metro plan add-pricing-dimension-value --action-code FEATURE_X --display-name "Feature X" --price 100 --sandbox --json
metro validate --shape dimensional-rate-card --file dimension.json --json
metro rate-card dimensional preview --file dimension.json --sandbox --json
```

The shape requires one USAGE product, a compound group key on the billable metric, `FLAT` rates with `entitled: true`, and a verified group key. Apply through the shape is blocked in this release; add the new rates with `metro ratecard add-rates`. To roll one value across several cards, run `metro plan rollout-dimension-across-cards --sandbox --json` after `metro catalog bind`.

## Readback

metro checks the card's name and aliases, then finds every rate through `getRates`:

```text
Applied (sandbox).
Read-back (expected vs actual):
  ok   rate_card.name: expected "Acme Standard" actual "Acme Standard"
  ok   rate_card.aliases: expected ["acme_standard"] actual ["acme_standard"]
  ok   rate 00000000-0000-4000-8000-000000000010@2026-12-01T00:00:00.000Z: expected {"entitled":true,"rate_type":"SUBSCRIPTION","price":4900} actual {...same...}
  ok   rate 00000000-0000-4000-8000-000000000011@2026-12-01T00:00:00.000Z: expected {"entitled":true,"rate_type":"FLAT","price":2} actual {...same...}
```

Sample output is from metro's test suite; IDs are placeholders. Next: put the card in a [package](/docs/guides/provision-a-package).
