Plans and rate cards
Change plans and rate cards, including dimensional rate cards.
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: 2metro ratecard create preflight --file=ratecard.yaml --sandbox --out=plans/ratecard.jsonmetro 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.jsonWhat 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
SUBSCRIPTIONrate must point at aSUBSCRIPTIONproduct. addRatesonly, 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:98ebc15e78183261ab2d2405a404baeb4648ff08a9178473dfe05543f182d868Apply
metro ratecard create apply --file plans/ratecard.json --sandbox \
--confirm-hash sha256:98ebc15e78183261ab2d2405a404baeb4648ff08a9178473dfe05543f182d868 \
--apply --confirm-live-apply=I_UNDERSTAND_THIS_WILL_WRITE_LIVE_METRONOME_SANDBOXAdd 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.jsonIt 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 --jsonThe 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.