Create a contract

Create a contract from a package, with preview and readback proof.

View .md

Goal

Put Acme Corp on the growth package from 1 December 2026, 00:00 UTC. The customer and the package already exist; see Provision a package.

Preview

metro contract create preflight --ingest-alias=acme-corp --package-alias=growth \
  --starting-at=2026-12-01T00:00:00.000Z --sandbox --out=plans/contract.json
metro contract create preflight (sandbox, key sha256:80f0714120b8) - read-only, nothing written to Metronome.
  contract: null -> {"customer_id":"00000000-0000-4000-8000-000000000101","starting_at":"2026-12-01T00:00:00.000Z","package_id":"00000000-0000-4000-8000-000000000104","uniqueness_key":"metro-contract-33ff039a22b5b61ae9e1c3fc"}
Steps: POST /v1/contracts/create
WARNING [MTR-MOVE-EMPTY-PACKAGE-001]: Package 00000000-0000-4000-8000-000000000104 has no commits, credits, subscriptions or scheduled charges.
Payload hash: sha256:...
Wrote plan: ./plans/contract.json

What preflight does:

  • Pins the package. It resolves growth to the package live at starting_at and writes that package_id into the plan. If the alias moves before you apply, you still get the package you previewed.
  • Sends only allowed fields. A package contract takes customer_id, starting_at, package_id, uniqueness_key, transition and custom_fields. Anything else returns 400 from Metronome, so metro doesn't send it.
  • Refuses overlaps. Two concurrent contracts bill twice, so preflight refuses one that overlaps an existing contract. Pass --allow-concurrent only if that is what you want. To move a customer between packages, use Move a customer.
  • Needs an hour. --starting-at must fall exactly on a UTC hour.

The warning above means the package bills nothing by itself yet. That's expected for a usage-only package; read it before you confirm.

A contract without a package

Write a spec with rate_card_id or rate_card_alias, plus subscriptions[], commits, billing_provider_configuration and the other create fields:

metro contract create preflight --file=contract.yaml --customer-id=00000000-0000-4000-8000-000000000101 --sandbox --out=plans/contract.json

Every subscription in the spec is checked the same way as in a package.

Confirm

Show the human the customer, the package, the start date, any warning and the hash. Then dry-run:

metro contract create apply --file plans/contract.json --sandbox --confirm-hash sha256:...

Apply

metro contract create apply --file plans/contract.json --sandbox --confirm-hash sha256:... \
  --apply --confirm-live-apply=I_UNDERSTAND_THIS_WILL_WRITE_LIVE_METRONOME_SANDBOX

Readback

Applied (sandbox).
Read-back (expected vs actual):
  ok   contract.id: expected "present" actual "present"
  ok   contract.customer_id: expected "00000000-0000-4000-8000-000000000101" actual "00000000-0000-4000-8000-000000000101"
  ok   contract.starting_at: expected "2026-12-01T00:00:00.000Z" actual "2026-12-01T00:00:00.000Z"
  ok   contract.uniqueness_key: expected "metro-contract-33ff039a22b5b61ae9e1c3fc" actual "metro-contract-33ff039a22b5b61ae9e1c3fc"
  ok   contract.package_id: expected "00000000-0000-4000-8000-000000000104" actual "00000000-0000-4000-8000-000000000104"

For a direct contract with subscriptions, the readback also checks the subscription count, each product and frequency, and the initial quantity. Confirm the result from the customer's side with metro ops who "Acme Corp" --sandbox.

On this page