# Contracts

A contract is the commercial agreement every other billing object hangs off:
subscriptions bill on it, credit is held against it, metered usage is priced
under it and every invoice names it. It belongs to one of your customers (a
`sellerCustomerId` from `POST /customers`) and bills in one currency for its
whole life — credit and invoices under it are never converted.

## Versions, not edits

A contract is a stable id plus an append-only history of **versions**. Nothing
is ever updated in place: every change — a status move, a new end date, a
different set of components — is a new version appended on top of the last,
and every earlier version stays readable. `GET /contracts` shows the current
version of each contract you own, with `version` saying how many there are.

That is what an amendment is: **a compare-and-swap on the version number**.
You send the `expectedBaseVersion` you last read; if the contract has moved on
since, the amendment answers `422` and you re-read before deciding again.
Two integrations amending the same contract can therefore never silently
overwrite each other.

## Status

`draft` → `active` → `suspended` ⇄ `active` → `closed`. A contract starts in
`draft`. `closed` is terminal: nothing about a closed contract can be amended.
A transition the state machine does not allow (anything out of `closed`, or
`draft` straight to `suspended`) answers `422`.

**Closing a contract does not cancel its subscriptions.** The status is a
record of the commercial relationship; no billing engine reads it, so a
subscription on a closed contract keeps renewing and keeps issuing invoices.
Cancel each one with `POST /billing-api/v1/subscriptions/{subscriptionId}/cancel`
FIRST, then close the contract.

## Endpoints

- `GET /billing-api/v1/contracts` — the current version of every contract you
  own, keyset-paged by contract id.
- `POST /billing-api/v1/contracts` — create one, in `draft`, for one of your
  customers. **`Idempotency-Key` is required**: the same key returns the
  contract the first call created (`200` instead of `201`); the same key
  answers `422`, naming the field, when the request differs in its
  `sellerCustomerId`, `currency` or the `lineItems` themselves — compared line by line on
  `componentType`, `componentConfig` and `quantity`, order-insensitively — or in the
  `startDate` or `endDate` as the key first created it. It also carries an EXTRA per-seller
  quota of its own — 20 create requests a rolling day by default (a refused or replayed request counts too), answered as `429`
  with `Retry-After` — bounding how fast one key can open contracts, each of
  which allocates a number from a sequence shared across sellers.
- `POST /billing-api/v1/contracts/{contractId}/amendments` — append a version:
  a status move, new dates, a replaced component set, or the counterparty's
  details as agreed. `expectedBaseVersion` is required and is the concurrency
  guard; there is no `Idempotency-Key`, because a repeat of a successful
  amendment fails the version check by construction. This one carries an EXTRA
  per-seller quota too — 200 amend requests a rolling day by default (a stale `expectedBaseVersion` counts too) — because
  every accepted amendment appends a version that can never be deleted.
- `GET /billing-api/v1/contracts/{contractId}/usage` and
  `GET /billing-api/v1/contracts/{contractId}/credits` — what has been metered
  and what stored value is held under the contract.

Both write quotas, and the invoice issue quota, **fail closed** — while the
limiter is unreachable they answer `503` with `Retry-After` and the write does
not happen; see [Errors → rate limiting](/billing-api/v1/docs/errors#when-the-write-quotas-fail-closed).

## Components

`lineItems` on a contract are its **components**: what the agreement says is
being sold, each with a `componentType`, a `quantity` (decimal string) and an
optional `componentConfig` the engine reads for that type. They are
snapshotted per version — an amendment either carries the current set forward
(omit `lineItems`) or replaces it whole (send the full new set; `[]` clears
it). There is no partial edit, for the same reason there is no partial edit
of anything else here: the version is the audit trail.

A contract may have no components at all. Its billing is then defined by the
subscriptions attached to it (`POST /contracts/{contractId}/subscriptions`)
and the metered prices set under it.

## What the seller key may not do

Contracts created through this API record no dashboard user as their author
(`createdBy` is `0`): the seller key is the actor, and the audit trail names
the key's seller account. Contract PDFs, tax profiles and the backoffice
review of a contract stay dashboard operations.
