Create a contract
Create a contract from a package, with preview and readback proof.
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.jsonmetro 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.jsonWhat preflight does:
- Pins the package. It resolves
growthto the package live atstarting_atand writes thatpackage_idinto 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,transitionandcustom_fields. Anything else returns400from 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-concurrentonly if that is what you want. To move a customer between packages, use Move a customer. - Needs an hour.
--starting-atmust 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.jsonEvery 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_SANDBOXReadback
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.