Plans and rate cards

Change plans and rate cards, including dimensional rate cards.

View .md

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.

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.

# 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
metro ratecard create preflight --file=ratecard.yaml --sandbox --out=plans/ratecard.json
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.

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

Apply

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:

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:

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:

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.

On this page