# Subscriptions and seats

> Add subscriptions and change quantities or seats on a contract.

Source: https://metrocli.com/docs/guides/subscriptions-and-seats/ · Docs index: https://metrocli.com/llms.txt

## Goal

Give Acme Corp 10 monthly seats from 1 December 2026, then raise them to 12 from 1 January 2027. See [Subscriptions & seats](/docs/concepts/subscriptions-and-seats).

## Preview

Add the subscription:

```bash
metro subscription add preflight --ingest-alias=acme-corp \
  --product-id=00000000-0000-4000-8000-000000000010 --billing-frequency=MONTHLY \
  --quantity=10 --starting-at=2026-12-01T00:00:00.000Z --sandbox --out=plans/sub.json
```

```text
metro subscription add preflight (sandbox, key sha256:80f0714120b8) - read-only, nothing written to Metronome.
  contract 00000000-0000-4000-8000-000000000105.subscriptions: 0 -> 1
Steps: POST /v2/contracts/edit
Payload hash: sha256:...
```

The dry run shows the `add_subscriptions` body metro will send:

```json
{"customer_id":"00000000-0000-4000-8000-000000000101","contract_id":"00000000-0000-4000-8000-000000000105",
 "add_subscriptions":[{"subscription_rate":{"product_id":"00000000-0000-4000-8000-000000000010","billing_frequency":"MONTHLY"},
   "collection_schedule":"ADVANCE","proration":{"is_prorated":true,"invoice_behavior":"BILL_ON_NEXT_COLLECTION_DATE"},
   "quantity_management_mode":"QUANTITY_ONLY","initial_quantity":10,"starting_at":"2026-12-01T00:00:00.000Z"}],
 "uniqueness_key":"metro-edit-9ab86777e09e8ede13fee5e3"}
```

Preflight refuses when the product isn't a `SUBSCRIPTION` product, when the contract's rate card has no entitled `SUBSCRIPTION` rate for it at that frequency, or when the contract already has that subscription (pass `--allow-duplicate` to add it anyway). If the customer has more than one contract, pass `--contract-id=`.

For a seat-based subscription, or any field the flags don't cover, pass a full spec with `--file=subscription.yaml`. Your Metronome account needs contract editing enabled.

Then the quantity change:

```bash
metro subscription set-quantity preflight --ingest-alias=acme-corp \
  --subscription-id=00000000-0000-4000-8000-000000000106 --quantity=12 \
  --starting-at=2027-01-01T00:00:00.000Z --sandbox --out=plans/qty.json
```

```text
  subscription 00000000-0000-4000-8000-000000000106 quantity @ 2027-01-01T00:00:00.000Z: 10 -> 12
Steps: POST /v2/contracts/edit
```

`--delta=2` works too. metro always sends an absolute `quantity`, computed from the current schedule, so apply writes exactly what you previewed.

### Seats

For a `SEAT_BASED` subscription, change seats instead of quantity:

```bash
metro subscription seats preflight --ingest-alias=acme-corp \
  --subscription-id=00000000-0000-4000-8000-000000000106 \
  --starting-at=2027-01-01T00:00:00.000Z --add-seat-ids=user-41,user-42 --sandbox
```

`--remove-seat-ids=`, `--add-unassigned=<n>` and `--remove-unassigned=<n>` work the same way.

## Confirm

Show the human the contract, the product, the quantity before and after, and the hash. Increases are prorated onto the next invoice by default; decreases take effect at the next billing boundary. Then dry-run:

```bash
metro subscription set-quantity apply --file plans/qty.json --sandbox --confirm-hash sha256:...
```

## Apply

```bash
metro subscription add apply --file plans/sub.json --sandbox --confirm-hash sha256:... \
  --apply --confirm-live-apply=I_UNDERSTAND_THIS_WILL_WRITE_LIVE_METRONOME_SANDBOX
metro subscription set-quantity apply --file plans/qty.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   quantity at 2027-01-01T00:00:00.000Z: expected 12 actual 12
```

`subscription add` checks the subscription count for that product and frequency, and the quantity at its start. `subscription seats` reads the seats history and checks the assigned seat IDs; it checks the total only when you change unassigned seats alone, because assigned seats fill unassigned ones first.

A failed readback looks like this and exits `3`:

```text
Applied, but read-back FAILED (sandbox).
Read-back (expected vs actual):
  FAIL quantity at 2027-02-01T00:00:00.000Z: expected 15 actual 12
```

Don't re-run the plan. Read the contract fresh with `metro ops who "Acme Corp" --sandbox`, find the cause, then preflight again.
