# Create a contract

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

Source: https://metrocli.com/docs/guides/create-a-contract/ · Docs index: https://metrocli.com/llms.txt

## 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](/docs/guides/provision-a-package).

## Preview

```bash
metro contract create preflight --ingest-alias=acme-corp --package-alias=growth \
  --starting-at=2026-12-01T00:00:00.000Z --sandbox --out=plans/contract.json
```

```text
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](/docs/guides/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:

```bash
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:

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

## Apply

```bash
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

```text
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`.
