# billing (/docs/reference/api/billing)

> Billing tells an organization what it spends, on what, and who spent it: usage recorded on meters is priced from a rate card per billing account and month, the…



Billing tells an organization what it spends, on what, and who spent it: usage recorded on meters is priced from a
rate card per billing account and month, then shared out to the people, agents, teams, departments and projects that
produced it. Budgets alert and can refuse usage, and the platform issues monthly invoices (statements) with credit
applied. Invoicing works like Stripe or Orb: drafts, finalized, paid, uncollectible and void invoices; one-off invoice
items; payments and credit notes; plans with fees, seats and meter prices; subscriptions with trials and prorations;
coupons; payment reminders and a printable invoice. Server code records usage, takes payments and runs the jobs
through `iam.billing` (no credential). The repository guide is `docs/billing.md`.

## How spend is computed [#how-spend-is-computed]

A **billing account** is an organization, or a tenant below it with a billing profile of its own. For each account and
month, every meter's total is priced once (tiers and free units apply to the account's total), and the cost is shared
out to the daily roll-ups of usage by quantity; for `unique` meters every person or agent costs the same. Reported
meters carry their own cost per event. Person, team, department and project spend therefore add up to the account's
charges. Meters an account defines for itself are chargeback only (`internal`) and never appear on a statement.

Usage is attributed when it is recorded: the identity, its direct teams (split evenly by default, see the `billing`
option `teamAttribution`), and its department (or its first team's). An agent's usage counts toward its sponsor's
teams and department. Money is in micros of the deployment currency (`costMicros`), with rounded `amount` fields in
currency units.

## Permissions [#permissions]

`iam:billing:read` reads spend, budgets, credits, profiles, invoices, plans, subscriptions and discounts;
`iam:billing:manage` defines meters and prices in the tenant, budgets and profiles, and lets an account's billing
managers subscribe to self-serve plans and redeem coupon codes; `iam:billing:record` records usage. Resources are
`iam/billing`, `iam/billing/meters/{key}`, `iam/billing/budgets`, `iam/billing/profile`, `iam/billing/credits`,
`iam/billing/statements`, `iam/billing/invoice-items`, `iam/billing/plans`, `iam/billing/subscriptions`,
`iam/billing/coupons` and `iam/billing/discounts`. Credits, closing a month, invoice items, payments, credit notes,
plans and coupons are for root administrators only; they act on any account's invoice by calling on the root tenant.

| Method                                      | What it does                                                                                                                            | Access     |
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| [`accounts`](#accounts)                     | Lists the billing accounts in the tenant's subtree with this month's billable spend, projection, available credit and latest statement. | Credential |
| [`anomalies`](#anomalies)                   | Lists the people, teams and meters whose spend on one day jumped far above their usual daily spend.                                     | Credential |
| [`cancelSubscription`](#cancelsubscription) | Cancels a subscription at the end of the month (default) or now.                                                                        | Credential |
| [`changePlan`](#changeplan)                 | Moves a subscription to another plan now, keeping its seats and what is left of its trial.                                              | Credential |
| [`check`](#check)                           | Reports whether the caller's own usage is within every enforced budget that covers it.                                                  | Credential |
| [`closePeriod`](#closeperiod)               | Issues statements for a month that has ended, for every billing account or only `accountId`'s.                                          | Credential |
| [`createBudget`](#createbudget)             | Creates a spend budget for the tenant, a tenant below it, a team, a department or a person, per month, quarter or year.                 | Credential |
| [`createCoupon`](#createcoupon)             | Creates a coupon accounts can redeem by code.                                                                                           | Credential |
| [`createCreditNote`](#createcreditnote)     | Issues a credit note against a finalized, paid or uncollectible invoice.                                                                | Credential |
| [`createInvoiceItem`](#createinvoiceitem)   | Adds a one-off charge, or with a negative `amount` a credit, to the billing account's next invoice.                                     | Credential |
| [`createMeter`](#createmeter)               | Defines a usage meter: a platform meter on the root tenant, a chargeback meter for the tenant's subtree elsewhere.                      | Credential |
| [`createPlan`](#createplan)                 | Defines a plan on the platform (root) tenant.                                                                                           | Credential |
| [`deactivateCoupon`](#deactivatecoupon)     | Stops a coupon (by code) from being redeemed; accounts that redeemed it keep their discount.                                            | Credential |
| [`deleteBudget`](#deletebudget)             | Deletes a budget and its alert history.                                                                                                 | Credential |
| [`deleteInvoiceItem`](#deleteinvoiceitem)   | Deletes a pending invoice item.                                                                                                         | Credential |
| [`deleteMeter`](#deletemeter)               | Deletes a meter that has never recorded usage, with its prices.                                                                         | Credential |
| [`deleteProfile`](#deleteprofile)           | Removes a billing profile, so the tenant's usage rolls into its parent's account again.                                                 | Credential |
| [`departmentSpend`](#departmentspend)       | Reports a department's spend, with the departments below it, grouped by `identity` by default.                                          | Credential |
| [`exportSpend`](#exportspend)               | Returns a spend report as a CSV file: one row per group with the amount, share, events and a column per meter's quantity.               | Credential |
| [`exportStatement`](#exportstatement)       | Returns a statement as a CSV file: its lines, credit and total, then the breakdown by project, team, department and person.             | Credential |
| [`finalizeInvoice`](#finalizeinvoice)       | Finalizes a draft invoice: recomputes it with the latest usage and invoice items, numbers it, seals it and emails it.                   | Credential |
| [`getProfile`](#getprofile)                 | Returns the billing profile of the tenant (or of `targetTenantId` below it) and the account that pays for it.                           | Credential |
| [`getStatement`](#getstatement)             | Returns one statement with its lines, credit, breakdown and bill-to details, re-checking its content hash.                              | Credential |
| [`getTerms`](#getterms)                     | Returns the contract terms of the tenant's billing account: discount, minimum monthly commitment and tax.                               | Credential |
| [`grantCredit`](#grantcredit)               | Grants credit to a billing account, which its statements draw on, earliest expiry first.                                                | Credential |
| [`listBudgets`](#listbudgets)               | Lists the tenant's budgets with their spend, projection and the thresholds reached in the current window.                               | Credential |
| [`listCoupons`](#listcoupons)               | Lists the platform's coupons, newest first, with their redemptions.                                                                     | Credential |
| [`listCreditNotes`](#listcreditnotes)       | Lists the credit notes of the billing accounts in the tenant's subtree, or of one `statementId`, newest first.                          | Credential |
| [`listCredits`](#listcredits)               | Lists the credit of the tenant's billing account with the available balance.                                                            | Credential |
| [`listDiscounts`](#listdiscounts)           | Lists the discounts (redeemed coupons) of the billing accounts in the tenant's subtree.                                                 | Credential |
| [`listInvoiceItems`](#listinvoiceitems)     | Lists invoice items of the billing accounts in the tenant's subtree, newest first, optionally by `status`.                              | Credential |
| [`listMeters`](#listmeters)                 | Lists the meters that reach the tenant with the price that applies to its billing account this month.                                   | Credential |
| [`listPlans`](#listplans)                   | Lists the platform's plans with their fees, seats and meter prices.                                                                     | Credential |
| [`listPrices`](#listprices)                 | Returns a meter's rate card as the tenant may see it, and the entry that prices its account this month.                                 | Credential |
| [`listStatements`](#liststatements)         | Lists the statements of the billing accounts in the tenant's subtree (all of them for the root), newest month first.                    | Credential |
| [`listSubscriptions`](#listsubscriptions)   | Lists the subscriptions of the billing accounts in the tenant's subtree, newest first.                                                  | Credential |
| [`listUsage`](#listusage)                   | Lists raw usage events recorded in the tenant for a month, newest first.                                                                | Credential |
| [`markPaid`](#markpaid)                     | Marks a finalized or uncollectible statement paid by recording a `manual` payment of the amount due, with an optional reference.        | Credential |
| [`markUncollectible`](#markuncollectible)   | Writes a finalized invoice off as uncollectible; a later payment still settles it.                                                      | Credential |
| [`mySpend`](#myspend)                       | Reports the caller's own spend: their usage and that of the agents they sponsor, with the budgets set on them.                          | Credential |
| [`previewStatement`](#previewstatement)     | Builds the statement a billing account would receive for a month (the current one so far by default), without issuing it.               | Credential |
| [`quote`](#quote)                           | Prices a quantity of a meter as a month total for the tenant's billing account.                                                         | Credential |
| [`record`](#record)                         | Records usage of a meter in the tenant, attributed to an identity, their teams and department, with optional tags.                      | Credential |
| [`recordMany`](#recordmany)                 | Records up to 100 usage events in one transaction, all or nothing.                                                                      | Credential |
| [`recordPayment`](#recordpayment)           | Records a payment against a finalized or uncollectible invoice.                                                                         | Credential |
| [`redeemCoupon`](#redeemcoupon)             | Redeems a coupon code for the billing account.                                                                                          | Credential |
| [`removeDiscount`](#removediscount)         | Ends a billing account's discount now.                                                                                                  | Credential |
| [`renderInvoice`](#renderinvoice)           | Returns an invoice as a standalone HTML page to print or save as PDF.                                                                   | Credential |
| [`resumeSubscription`](#resumesubscription) | Undoes a cancellation at the end of the month before it takes effect.                                                                   | Credential |
| [`revokeCredit`](#revokecredit)             | Withdraws what is left of a credit.                                                                                                     | Credential |
| [`setPrice`](#setprice)                     | Sets or removes a rate-card price for a meter the tenant defines: its list price, or a negotiated price for a tenant below it.          | Credential |
| [`setProfile`](#setprofile)                 | Creates or updates a billing profile: company, billing emails, tax ID, address, purchase order, cost center, payment terms.             | Credential |
| [`setTerms`](#setterms)                     | Sets a billing account's contract terms: a discount off the subtotal, a minimum monthly commitment, and the tax invoices add.           | Credential |
| [`spend`](#spend)                           | Reports the spend of the tenant and every tenant below it for a month, grouped and filtered.                                            | Credential |
| [`subscribe`](#subscribe)                   | Subscribes the billing account to a plan (id or key).                                                                                   | Credential |
| [`teamSpend`](#teamspend)                   | Reports a team's spend, with the teams below it, grouped by `identity` by default.                                                      | Credential |
| [`trend`](#trend)                           | Returns monthly totals for the last `months` months (1 to 24, default 6), with the filters of `spend`.                                  | Credential |
| [`updateBudget`](#updatebudget)             | Changes a budget's name, amount, period, meters, thresholds, alerts or enforcement; its subject stays.                                  | Credential |
| [`updateMeter`](#updatemeter)               | Renames a meter, changes its unit or description, or archives it.                                                                       | Credential |
| [`updatePlan`](#updateplan)                 | Changes a plan, by id or key: name, items, description, trial, self-serve, archived.                                                    | Credential |
| [`updateSubscription`](#updatesubscription) | Changes a subscription's seats.                                                                                                         | Credential |
| [`voidStatement`](#voidstatement)           | Voids a finalized statement: its credit, invoice items, coupons and advance-billed months come back and the month reopens.              | Credential |

## accounts [#accounts]

Lists the billing accounts in the tenant's subtree with this month's billable spend, projection, available credit and latest statement.

**HTTP:** `POST /api/iam/billing/accounts` (requires a credential) · **Browser client:** `client.billing.accounts()`

* **Permission:** `iam:billing:read` on `iam/billing`.
* **Audited as:** `iam:billing:read`.

On the root tenant this is every organization: the platform's receivables view. `monthToDateMicros` counts only
meters defined above the account (what a statement would bill); `totalMicros` includes the account's own chargeback
meters.

```ts title="Signature"
iam.api.billing.accounts(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<AccountOverview[]>
```

## anomalies [#anomalies]

Lists the people, teams and meters whose spend on one day jumped far above their usual daily spend.

**HTTP:** `POST /api/iam/billing/anomalies` (requires a credential) · **Browser client:** `client.billing.anomalies()`

* **Permission:** `iam:billing:read` on `iam/billing`.
* **Audited as:** `iam:billing:read`.
* **Errors:** `INVALID_INPUT` for a malformed `day`, `baselineDays` outside 3 to 90, `factor` outside 1.1 to 1000, or a
  negative `minimum`.

`day` defaults to yesterday in the billing time zone. A spike is spend at least `factor` (3) times the average over the
`baselineDays` (14) before it and at least `minimum` (10 currency units) more; spending with no baseline counts when it
reaches `minimum` (`factor: null`). The largest increases come first, at most 50. The daily job
`iam.billing.detectAnomalies()` alerts on them once each (`billing:anomaly`, `spend-anomaly` email).

```ts
const { anomalies } = await iam.api.billing.anomalies(admin, { tenantId, factor: 5 });
```

```ts title="Signature"
iam.api.billing.anomalies(
  credential: CredentialInput,
  input: { tenantId: string } & AnomalyOptions,
): Promise<{ tenantId: string; anomalies: SpendAnomaly[] }>
```

## cancelSubscription [#cancelsubscription]

Cancels a subscription at the end of the month (default) or now.

**HTTP:** `POST /api/iam/billing/cancelSubscription` (requires a credential) · **Browser client:** `client.billing.cancelSubscription()`

* **Permission:** `iam:billing:manage` on `iam/billing/subscriptions` (self-serve plans, at the month's end only), or
  a root administrator.
* **Audited as:** `billing:subscription-cancel`.
* **Errors:** `ACCESS_DENIED` (403) for an immediate cancellation by anyone but a root administrator;
  `INVALID_TRANSITION` (409) for an ended subscription.

At the month's end the subscription keeps running and `resumeSubscription` can undo it. With `atPeriodEnd: false` it
ends now and the unused part of this month's advance fees and seats is credited as invoice items; arrears items bill
the part of the month it ran.

```ts title="Signature"
iam.api.billing.cancelSubscription(
  credential: CredentialInput,
  input: { tenantId: string; subscriptionId: string; atPeriodEnd?: boolean },
): Promise<{ subscription: SubscriptionView; invoiceItems: InvoiceItemView[] }>
```

## changePlan [#changeplan]

Moves a subscription to another plan now, keeping its seats and what is left of its trial.

**HTTP:** `POST /api/iam/billing/changePlan` (requires a credential) · **Browser client:** `client.billing.changePlan()`

* **Permission:** `iam:billing:manage` on `iam/billing/subscriptions` when both plans are self-serve, or a root
  administrator.
* **Audited as:** `billing:subscription-plan-change`.
* **Errors:** `CONFLICT` (409) when the account already subscribes to the new plan; `INVALID_TRANSITION` (409) for an
  ended subscription or an archived plan; `INVALID_INPUT` for the same plan.

The old subscription ends and a new one starts. The old plan's unused advance charges are credited and the new plan's
charges for the rest of the month added, both as invoice items for the next invoice (`invoiceItems`).

```ts title="Signature"
iam.api.billing.changePlan(
  credential: CredentialInput,
  input: { tenantId: string; subscriptionId: string; plan: string },
): Promise<{ subscription: SubscriptionView; invoiceItems: InvoiceItemView[] }>
```

## check [#check]

Reports whether the caller's own usage is within every enforced budget that covers it.

**HTTP:** `POST /api/iam/billing/check` (requires a credential) · **Browser client:** `client.billing.check()`

* **Permission:** The caller's own session; the tenant must be the caller's tenant or one below it.
* **Audited as:** not audited.
* **Errors:** `ACCESS_DENIED` (403) for a tenant outside the caller's.

Covering budgets are enforced tenant budgets on the tenant or an ancestor, and the caller's own, their teams' (with
parent teams) and their department's (with the departments above it); `meter` narrows to budgets that count it.
`blockedBy` names the first spent budget. Statuses may lag recorded usage by up to 30 seconds. Server code checks any
identity with `iam.billing.check`.

```ts
const verdict = await client.billing.check({ tenantId, meter: 'api-calls' });
if (!verdict.allowed) showBudgetBanner(verdict.blockedBy);
```

```ts title="Signature"
iam.api.billing.check(
  credential: CredentialInput,
  input: { tenantId: string; meter?: string },
): Promise<SpendCheck>
```

## closePeriod [#closeperiod]

Issues statements for a month that has ended, for every billing account or only `accountId`'s.

**HTTP:** `POST /api/iam/billing/closePeriod` (requires a credential) · **Browser client:** `client.billing.closePeriod()`

* **Permission:** Root administrators only, called on the root tenant (`iam:billing:manage` on `iam/billing/periods`).
* **Audited as:** `iam:billing:manage`; each statement as `billing:statement`.
* **Errors:** `INVALID_INPUT` for the current or a future month, or when called on another tenant.

`period` defaults to last month. Each invoice bills the month's usage of platform meters, the subscriptions' fees
and seats (the month itself for arrears items, the next month for advance ones), and pending invoice items. Accounts
already invoiced for the month and accounts with nothing to bill are skipped (`skipped.existing`, `skipped.empty`), so
the job is safe to repeat. Coupons apply after the contract discount, credit earliest expiry first; invoices are
emailed (`billing-statement`) to the profile's billing emails or the owners, and raw usage events past their retention
are deleted (`sweptUsage`). With `draft: true` (or the option `billing.autoFinalize: false`) invoices are kept as
drafts, refreshed on every run, and listed in `drafted`; finalize them with `finalizeInvoice`. Schedulers call
`iam.billing.closePeriod()` instead.

```ts title="Signature"
iam.api.billing.closePeriod(
  credential: CredentialInput,
  input: { tenantId: string; period?: string; accountId?: string; draft?: boolean },
): Promise<ClosePeriodResult>
```

## createBudget [#createbudget]

Creates a spend budget for the tenant, a tenant below it, a team, a department or a person, per month, quarter or year.

**HTTP:** `POST /api/iam/billing/createBudget` (requires a credential) · **Browser client:** `client.billing.createBudget()`

* **Permission:** `iam:billing:manage` on `iam/billing/budgets`.
* **Audited as:** `billing:budget-create`.
* **Errors:** `CONFLICT` (409) for a name already used (case-insensitive); `LIMIT_EXCEEDED` (409) past 200 budgets;
  `NOT_FOUND` for a subject outside the tenant; `INVALID_INPUT` for an amount of 0, a malformed meter key, more than 10
  thresholds or notification addresses.

`amount` is in currency units. `thresholds` (default 50, 80, 100 percent) and `forecastAlerts` (default on) drive the
alerts that `iam.billing.checkBudgets()` sends once per window to the owners, the subject (the person, the team's
maintainers or the department head) and `notify.emails`. `enforce` makes covered usage fail once the budget is spent.
The result is the budget with its current standing.

```ts
await iam.api.billing.createBudget(admin, {
  tenantId,
  name: 'Platform team monthly',
  subjectType: 'team',
  subjectId: platformTeamId,
  amount: 600,
  notify: { emails: ['finance@acme.test'] },
});
```

```ts title="Signature"
iam.api.billing.createBudget(
  credential: CredentialInput,
  input: { tenantId: string } & BillingBudgetInput,
): Promise<BillingBudgetView>
```

## createCoupon [#createcoupon]

Creates a coupon accounts can redeem by code.

**HTTP:** `POST /api/iam/billing/createCoupon` (requires a credential) · **Browser client:** `client.billing.createCoupon()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/coupons`), called on the root tenant.
* **Audited as:** `billing:coupon`.
* **Errors:** `CONFLICT` (409) for a code already used; `INVALID_INPUT` for a code that is not 3 to 32 letters, digits,
  `-` or `_`, both or neither of `percentOff` and `amountOff`, a percentage outside 0 to 100, `durationInMonths`
  without `duration: 'repeating'`, or a `redeemBy` in the past.

Codes are stored in upper case. `duration` is `once` (default: the next invoice), `repeating` (invoices for
`durationInMonths` months from the month of redemption) or `forever`. `maxRedemptions` caps how many accounts may
redeem it.

```ts
await iam.api.billing.createCoupon(root, {
  tenantId: rootTenantId,
  code: 'LAUNCH20',
  percentOff: 20,
  duration: 'repeating',
  durationInMonths: 3,
});
```

```ts title="Signature"
iam.api.billing.createCoupon(
  credential: CredentialInput,
  input: {
    tenantId: string;
    code: string;
    name?: string;
    percentOff?: number;
    amountOff?: number;
    duration?: BillingCoupon['duration'];
    durationInMonths?: number;
    maxRedemptions?: number;
    redeemBy?: number;
  },
): Promise<CouponView>
```

## createCreditNote [#createcreditnote]

Issues a credit note against a finalized, paid or uncollectible invoice.

**HTTP:** `POST /api/iam/billing/createCreditNote` (requires a credential) · **Browser client:** `client.billing.createCreditNote()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`).
* **Audited as:** `billing:credit-note`.
* **Errors:** `INVALID_INPUT` for an amount above what is left to credit, an unknown `reason`, or a non-boolean
  `refund`; `INVALID_TRANSITION` (409) for a draft or void invoice.

`amount` defaults to everything not yet credited. The note first reduces the amount due; the rest (a part already
paid) becomes account credit, or with `refund: true` is recorded as refunded outside Better IAM. `applied` shows the
split. Notes are numbered `{invoice}-CN-01`, `-CN-02`, ...; `reason` is `duplicate`, `fraudulent`, `order_change`,
`product_unsatisfactory` or `other` (default), with an optional `memo`. An invoice the notes and payments cover is paid.

```ts title="Signature"
iam.api.billing.createCreditNote(
  credential: CredentialInput,
  input: {
    tenantId: string;
    statementId: string;
    amount?: number;
    reason?: BillingCreditNote['reason'];
    memo?: string;
    refund?: boolean;
  },
): Promise<{ statement: StatementSummary; creditNote: CreditNoteView }>
```

## createInvoiceItem [#createinvoiceitem]

Adds a one-off charge, or with a negative `amount` a credit, to the billing account's next invoice.

**HTTP:** `POST /api/iam/billing/createInvoiceItem` (requires a credential) · **Browser client:** `client.billing.createInvoiceItem()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/invoice-items`), called on the account.
* **Audited as:** `billing:invoice-item`.
* **Errors:** `INVALID_INPUT` when the tenant is not a billing account, for an empty description, an amount beyond one
  billion, a quantity of 0, or more than 20 metadata entries; `BILLING_PERIOD_CLOSED` (409) for a `period` already
  invoiced.

`amount` is per unit in currency units and `quantity` defaults to 1; the item's total is rounded to the cent. `period`
bills it on the invoice for that month instead of the next one. When credit items exceed an invoice's charges the
invoice totals 0 and the rest becomes account credit.

```ts
await iam.api.billing.createInvoiceItem(root, {
  tenantId: acmeId,
  description: 'Onboarding workshop',
  amount: 500,
});
```

```ts title="Signature"
iam.api.billing.createInvoiceItem(
  credential: CredentialInput,
  input: {
    tenantId: string;
    description: string;
    amount: number;
    quantity?: number;
    period?: string;
    metadata?: Record<string, string>;
  },
): Promise<InvoiceItemView>
```

## createMeter [#createmeter]

Defines a usage meter: a platform meter on the root tenant, a chargeback meter for the tenant's subtree elsewhere.

**HTTP:** `POST /api/iam/billing/createMeter` (requires a credential) · **Browser client:** `client.billing.createMeter()`

* **Permission:** `iam:billing:manage` on `iam/billing/meters/{key}`.
* **Audited as:** `billing:meter-create`.
* **Errors:** `CONFLICT` (409) when a meter with the key already reaches the tenant; `LIMIT_EXCEEDED` (409) past 100
  meters; `INVALID_INPUT` for a malformed key or a `unique` reported meter.

Keys are 1 to 64 lowercase letters, digits, dots, underscores or hyphens, starting with a letter. `aggregation` is
`sum` (default) or `unique` (distinct people and agents per month); `pricing` is `rate-card` (default) or `reported`
(each event carries its cost). Neither can change later.

```ts title="Signature"
iam.api.billing.createMeter(
  credential: CredentialInput,
  input: {
    tenantId: string;
    key: string;
    name: string;
    unit?: string;
    description?: string;
    aggregation?: BillingMeter['aggregation'];
    pricing?: BillingMeter['pricing'];
  },
): Promise<MeterView>
```

## createPlan [#createplan]

Defines a plan on the platform (root) tenant.

**HTTP:** `POST /api/iam/billing/createPlan` (requires a credential) · **Browser client:** `client.billing.createPlan()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/plans`), called on the root tenant.
* **Audited as:** `billing:plan`.
* **Errors:** `CONFLICT` (409) for a key already used; `LIMIT_EXCEEDED` (409) past 100 plans; `INVALID_INPUT` for a
  malformed key, 0 or more than 20 items, duplicate item ids, two items pricing one meter, or a malformed price.

`items` are `fee` (`amount` per month), `seat` (`unitAmount` per seat and month, `includedSeats`), both billed in
`advance` (default) or `arrears`, and `usage` (`meter` with a `price` that replaces the rate card for subscribers).
`trialDays` (1 to 365) starts subscriptions with a free trial; `selfServe` lets account billing managers subscribe.

```ts
await iam.api.billing.createPlan(root, {
  tenantId: rootTenantId,
  key: 'team',
  name: 'Team',
  selfServe: true,
  items: [
    { id: 'platform', kind: 'fee', name: 'Platform fee', amount: 99 },
    { id: 'seats', kind: 'seat', name: 'Seats', unitAmount: 12, includedSeats: 3 },
  ],
});
```

```ts title="Signature"
iam.api.billing.createPlan(
  credential: CredentialInput,
  input: {
    tenantId: string;
    key: string;
    name: string;
    items: unknown[];
    description?: string;
    trialDays?: number;
    selfServe?: boolean;
  },
): Promise<PlanView>
```

## deactivateCoupon [#deactivatecoupon]

Stops a coupon (by code) from being redeemed; accounts that redeemed it keep their discount.

**HTTP:** `POST /api/iam/billing/deactivateCoupon` (requires a credential) · **Browser client:** `client.billing.deactivateCoupon()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/coupons`), called on the root tenant.
* **Audited as:** `billing:coupon-deactivate`.
* **Errors:** `NOT_FOUND` for an unknown code.

```ts title="Signature"
iam.api.billing.deactivateCoupon(
  credential: CredentialInput,
  input: { tenantId: string; code: string },
): Promise<CouponView>
```

## deleteBudget [#deletebudget]

Deletes a budget and its alert history.

**HTTP:** `POST /api/iam/billing/deleteBudget` (requires a credential) · **Browser client:** `client.billing.deleteBudget()`

* **Permission:** `iam:billing:manage` on `iam/billing/budgets`.
* **Audited as:** `billing:budget-delete`.
* **Errors:** `NOT_FOUND` for a budget of another tenant.

```ts title="Signature"
iam.api.billing.deleteBudget(
  credential: CredentialInput,
  input: { tenantId: string; budgetId: string },
): Promise<{ success: true }>
```

## deleteInvoiceItem [#deleteinvoiceitem]

Deletes a pending invoice item.

**HTTP:** `POST /api/iam/billing/deleteInvoiceItem` (requires a credential) · **Browser client:** `client.billing.deleteInvoiceItem()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/invoice-items`), called on the account.
* **Audited as:** `billing:invoice-item-delete`.
* **Errors:** `INVALID_TRANSITION` (409) for an item already on a finalized invoice (issue a credit note instead).

```ts title="Signature"
iam.api.billing.deleteInvoiceItem(
  credential: CredentialInput,
  input: { tenantId: string; itemId: string },
): Promise<{ success: true }>
```

## deleteMeter [#deletemeter]

Deletes a meter that has never recorded usage, with its prices.

**HTTP:** `POST /api/iam/billing/deleteMeter` (requires a credential) · **Browser client:** `client.billing.deleteMeter()`

* **Permission:** `iam:billing:manage` on `iam/billing/meters/{key}`.
* **Audited as:** `billing:meter-delete`.
* **Errors:** `RESOURCE_IN_USE` (409) once the meter has recorded usage (archive it with `updateMeter` instead);
  `NOT_FOUND` when the tenant does not define the key.

```ts title="Signature"
iam.api.billing.deleteMeter(
  credential: CredentialInput,
  input: { tenantId: string; key: string },
): Promise<{ success: true; removedPrices: number }>
```

## deleteProfile [#deleteprofile]

Removes a billing profile, so the tenant's usage rolls into its parent's account again.

**HTTP:** `POST /api/iam/billing/deleteProfile` (requires a credential) · **Browser client:** `client.billing.deleteProfile()`

* **Permission:** `iam:billing:manage` on `iam/billing/profile`; below an organization, called from an ancestor with
  `targetTenantId`.
* **Audited as:** `billing:profile-delete`.
* **Errors:** `NOT_FOUND` without a profile; `ACCESS_DENIED` (403) when a tenant below an organization removes its own.

```ts title="Signature"
iam.api.billing.deleteProfile(
  credential: CredentialInput,
  input: { tenantId: string; targetTenantId?: string },
): Promise<{ success: true }>
```

## departmentSpend [#departmentspend]

Reports a department's spend, with the departments below it, grouped by `identity` by default.

**HTTP:** `POST /api/iam/billing/departmentSpend` (requires a credential) · **Browser client:** `client.billing.departmentSpend()`

* **Permission:** The department's head (or the head of a department above it), or `iam:billing:read`.
* **Audited as:** `iam:billing:read`, with `metadata.via` `department-head` or `permission`.
* **Errors:** `ACCESS_DENIED` (403) for anyone else; `NOT_FOUND` for a department outside the tenant.

```ts title="Signature"
iam.api.billing.departmentSpend(
  credential: CredentialInput,
  input: {
    tenantId: string;
    departmentId: string;
    period?: string;
    groupBy?: SpendGroupBy;
  },
): Promise<SpendReport>
```

## exportSpend [#exportspend]

Returns a spend report as a CSV file: one row per group with the amount, share, events and a column per meter's quantity.

**HTTP:** `POST /api/iam/billing/exportSpend` (requires a credential) · **Browser client:** `client.billing.exportSpend()`

* **Permission:** `iam:billing:read` on `iam/billing`.
* **Audited as:** `iam:billing:read`.
* **Errors:** as `spend`.

It takes the same input as `spend` and returns `filename`, `contentType` and `body` (RFC 4180, CRLF line endings, a
final total row). Cells that a spreadsheet would run as a formula are prefixed with a quote.

```ts title="Signature"
iam.api.billing.exportSpend(
  credential: CredentialInput,
  input: { tenantId: string } & SpendQuery,
): Promise<CsvExport>
```

## exportStatement [#exportstatement]

Returns a statement as a CSV file: its lines, credit and total, then the breakdown by project, team, department and person.

**HTTP:** `POST /api/iam/billing/exportStatement` (requires a credential) · **Browser client:** `client.billing.exportStatement()`

* **Permission:** `iam:billing:read` on `iam/billing/statements`; the statement's account must be the tenant or below it.
* **Audited as:** `iam:billing:read`.
* **Errors:** `NOT_FOUND` for a statement outside the tenant's subtree.

The file is named after the statement number. Department rows carry their cost center.

```ts title="Signature"
iam.api.billing.exportStatement(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string },
): Promise<CsvExport>
```

## finalizeInvoice [#finalizeinvoice]

Finalizes a draft invoice: recomputes it with the latest usage and invoice items, numbers it, seals it and emails it.

**HTTP:** `POST /api/iam/billing/finalizeInvoice` (requires a credential) · **Browser client:** `client.billing.finalizeInvoice()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`), called on the account
  or the root tenant.
* **Audited as:** `billing:statement-finalize` and `billing:statement`.
* **Errors:** `INVALID_TRANSITION` (409) for an invoice that is not a draft, or a draft with nothing left to bill.

The invoice then consumes its credit, marks its invoice items `invoiced`, its subscriptions' advance months billed, and
its coupons used. An invoice with nothing to pay is `paid` on issue.

```ts title="Signature"
iam.api.billing.finalizeInvoice(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string },
): Promise<StatementSummary>
```

## getProfile [#getprofile]

Returns the billing profile of the tenant (or of `targetTenantId` below it) and the account that pays for it.

**HTTP:** `POST /api/iam/billing/getProfile` (requires a credential) · **Browser client:** `client.billing.getProfile()`

* **Permission:** `iam:billing:read` on `iam/billing/profile`.
* **Audited as:** `iam:billing:read`.

`account.inherited` is true when an ancestor pays; `profile` is null without a profile of its own.

```ts title="Signature"
iam.api.billing.getProfile(
  credential: CredentialInput,
  input: { tenantId: string; targetTenantId?: string },
): Promise<ProfileView>
```

## getStatement [#getstatement]

Returns one statement with its lines, credit, breakdown and bill-to details, re-checking its content hash.

**HTTP:** `POST /api/iam/billing/getStatement` (requires a credential) · **Browser client:** `client.billing.getStatement()`

* **Permission:** `iam:billing:read` on `iam/billing/statements`; the statement's account must be the tenant or below it.
* **Audited as:** `iam:billing:read`.
* **Errors:** `NOT_FOUND` for a statement outside the tenant's subtree.

`verified` is false when the stored content no longer matches the hash computed at issue. `overdue` is true for a
finalized statement past its due date.

```ts title="Signature"
iam.api.billing.getStatement(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string },
): Promise<{
  total: number;
  overdue: boolean;
  verified: boolean;
  number: string;
  status: InvoiceStatus;
  issuedAt: number;
  dueAt: number;
  hash: string;
  payments?: InvoicePayment[];
  amountPaidMicros?: number;
  creditNotesMicros?: number;
  invoiceItemIds?: string[];
  advanceBilled?: { subscriptionId: string; period: string }[];
  carryForward?: { creditId: string; amountMicros: number };
  reminders?: { days: number; at: number; recipients: number }[];
  paidAt?: number;
  paidBy?: string;
  paymentReference?: string;
  voidedAt?: number;
  voidedBy?: string;
  voidReason?: string;
  markedUncollectibleAt?: number;
  id: string;
  tenantId: string;
  uniqueKey?: string;
  period: string;
  currency: string;
  periodStart: number;
  periodEnd: number;
  lines: StatementLine[];
  billingReason?: 'period' | 'subscription' | 'manual';
  subtotalMicros: number;
  coupons?: { discountId: string; code: string; name: string; amountMicros: number }[];
  discount?: { percent: number; amountMicros: number };
  commitment?: { minimumMicros: number; trueUpMicros: number };
  creditsMicros: number;
  creditsApplied: { creditId: string; amountMicros: number }[];
  tax?: { label: string; ratePercent: number; amountMicros: number };
  totalMicros: number;
  breakdown: {
    tenants: StatementAllocation[];
    teams: StatementAllocation[];
    departments: (StatementAllocation & { costCenter?: string })[];
    identities: StatementAllocation[];
  };
  billTo: {
    name: string;
    companyName?: string;
    taxId?: string;
    address?: string;
    purchaseOrder?: string;
    costCenter?: string;
    emails: string[];
  };
}>
```

## getTerms [#getterms]

Returns the contract terms of the tenant's billing account: discount, minimum monthly commitment and tax.

**HTTP:** `POST /api/iam/billing/getTerms` (requires a credential) · **Browser client:** `client.billing.getTerms()`

* **Permission:** `iam:billing:read` on `iam/billing/terms`.
* **Audited as:** `iam:billing:read`.

When an ancestor pays for the tenant the result only says `inherited: true`; without terms it lists none.

```ts title="Signature"
iam.api.billing.getTerms(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<TermsView>
```

## grantCredit [#grantcredit]

Grants credit to a billing account, which its statements draw on, earliest expiry first.

**HTTP:** `POST /api/iam/billing/grantCredit` (requires a credential) · **Browser client:** `client.billing.grantCredit()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/credits`).
* **Audited as:** `billing:credit-grant`.
* **Errors:** `INVALID_INPUT` when the tenant is not a billing account (an organization or a tenant with a profile), for
  an amount of 0, or an `expiresAt` in the past.

```ts
await iam.api.billing.grantCredit(root, { tenantId: acmeId, amount: 100, reason: 'Launch promotion' });
```

```ts title="Signature"
iam.api.billing.grantCredit(
  credential: CredentialInput,
  input: { tenantId: string; amount: number; reason: string; expiresAt?: number },
): Promise<CreditView>
```

## listBudgets [#listbudgets]

Lists the tenant's budgets with their spend, projection and the thresholds reached in the current window.

**HTTP:** `POST /api/iam/billing/listBudgets` (requires a credential) · **Browser client:** `client.billing.listBudgets()`

* **Permission:** `iam:billing:read` on `iam/billing/budgets`.
* **Audited as:** `iam:billing:read`.

```ts title="Signature"
iam.api.billing.listBudgets(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<BillingBudgetView[]>
```

## listCoupons [#listcoupons]

Lists the platform's coupons, newest first, with their redemptions.

**HTTP:** `POST /api/iam/billing/listCoupons` (requires a credential) · **Browser client:** `client.billing.listCoupons()`

* **Permission:** Root administrators only (`iam:billing:read` on `iam/billing/coupons`), called on the root tenant.
* **Audited as:** `iam:billing:read`.

`active` is false for deactivated coupons, those past `redeemBy`, and those out of redemptions.

```ts title="Signature"
iam.api.billing.listCoupons(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<CouponView[]>
```

## listCreditNotes [#listcreditnotes]

Lists the credit notes of the billing accounts in the tenant's subtree, or of one `statementId`, newest first.

**HTTP:** `POST /api/iam/billing/listCreditNotes` (requires a credential) · **Browser client:** `client.billing.listCreditNotes()`

* **Permission:** `iam:billing:read` on `iam/billing/statements`.
* **Audited as:** `iam:billing:read`.

```ts title="Signature"
iam.api.billing.listCreditNotes(
  credential: CredentialInput,
  input: { tenantId: string; statementId?: string },
): Promise<CreditNoteView[]>
```

## listCredits [#listcredits]

Lists the credit of the tenant's billing account with the available balance.

**HTTP:** `POST /api/iam/billing/listCredits` (requires a credential) · **Browser client:** `client.billing.listCredits()`

* **Permission:** `iam:billing:read` on `iam/billing/credits`.
* **Audited as:** `iam:billing:read`.

When an ancestor pays for the tenant the list is empty and `inherited` is true.

```ts title="Signature"
iam.api.billing.listCredits(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<{
  accountId: string;
  inherited: boolean;
  balanceMicros: number;
  balance: number;
  credits: CreditView[];
}>
```

## listDiscounts [#listdiscounts]

Lists the discounts (redeemed coupons) of the billing accounts in the tenant's subtree.

**HTTP:** `POST /api/iam/billing/listDiscounts` (requires a credential) · **Browser client:** `client.billing.listDiscounts()`

* **Permission:** `iam:billing:read` on `iam/billing/discounts`.
* **Audited as:** `iam:billing:read`.

`appliedInvoices` counts the invoices a discount reduced; `active` says whether it applies to this month's invoice.

```ts title="Signature"
iam.api.billing.listDiscounts(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<DiscountView[]>
```

## listInvoiceItems [#listinvoiceitems]

Lists invoice items of the billing accounts in the tenant's subtree, newest first, optionally by `status`.

**HTTP:** `POST /api/iam/billing/listInvoiceItems` (requires a credential) · **Browser client:** `client.billing.listInvoiceItems()`

* **Permission:** `iam:billing:read` on `iam/billing/invoice-items`.
* **Audited as:** `iam:billing:read`.
* **Errors:** `INVALID_INPUT` for a `status` other than `pending` or `invoiced`.

`pending` items wait for the account's next invoice (or the one for their `period`); `invoiced` items carry the
`statementId` that billed them. `source: 'proration'` items come from subscription changes.

```ts title="Signature"
iam.api.billing.listInvoiceItems(
  credential: CredentialInput,
  input: { tenantId: string; status?: BillingInvoiceItem['status'] },
): Promise<InvoiceItemView[]>
```

## listMeters [#listmeters]

Lists the meters that reach the tenant with the price that applies to its billing account this month.

**HTTP:** `POST /api/iam/billing/listMeters` (requires a credential) · **Browser client:** `client.billing.listMeters()`

* **Permission:** `iam:billing:read` on `iam/billing`.
* **Audited as:** `iam:billing:read`.

`scope` is `platform` for root meters; `inherited` marks meters an ancestor defines.

```ts title="Signature"
iam.api.billing.listMeters(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<MeterView[]>
```

## listPlans [#listplans]

Lists the platform's plans with their fees, seats and meter prices.

**HTTP:** `POST /api/iam/billing/listPlans` (requires a credential) · **Browser client:** `client.billing.listPlans()`

* **Permission:** `iam:billing:read` on `iam/billing/plans`.
* **Audited as:** `iam:billing:read`.

Tenants see plans that are not archived. Root administrators on the root tenant also get `subscribers` (live
subscriptions) and, with `includeArchived`, archived plans. Amounts are in currency units.

```ts title="Signature"
iam.api.billing.listPlans(
  credential: CredentialInput,
  input: { tenantId: string; includeArchived?: boolean },
): Promise<PlanView[]>
```

## listPrices [#listprices]

Returns a meter's rate card as the tenant may see it, and the entry that prices its account this month.

**HTTP:** `POST /api/iam/billing/listPrices` (requires a credential) · **Browser client:** `client.billing.listPrices()`

* **Permission:** `iam:billing:read` on `iam/billing/meters/{key}`.
* **Audited as:** `iam:billing:read`.
* **Errors:** `NOT_FOUND` when no meter with the key reaches the tenant.

Entries for the tenant, its ancestors (list prices) and tenants below it are listed, newest first; negotiated prices
for other organizations are not.

```ts title="Signature"
iam.api.billing.listPrices(
  credential: CredentialInput,
  input: { tenantId: string; meter: string },
): Promise<{ effective?: PriceView | undefined; meter: MeterView; entries: PriceView[] }>
```

## listStatements [#liststatements]

Lists the statements of the billing accounts in the tenant's subtree (all of them for the root), newest month first.

**HTTP:** `POST /api/iam/billing/listStatements` (requires a credential) · **Browser client:** `client.billing.listStatements()`

* **Permission:** `iam:billing:read` on `iam/billing/statements`.
* **Audited as:** `iam:billing:read`.

Filter by `status` (`finalized`, `paid`, `void`) or `period`.

```ts title="Signature"
iam.api.billing.listStatements(
  credential: CredentialInput,
  input: { tenantId: string; status?: BillingStatement['status']; period?: string },
): Promise<StatementSummary[]>
```

## listSubscriptions [#listsubscriptions]

Lists the subscriptions of the billing accounts in the tenant's subtree, newest first.

**HTTP:** `POST /api/iam/billing/listSubscriptions` (requires a credential) · **Browser client:** `client.billing.listSubscriptions()`

* **Permission:** `iam:billing:read` on `iam/billing/subscriptions`.
* **Audited as:** `iam:billing:read`.

Ended subscriptions are left out unless `includeEnded`. `status` is `trialing`, `active` or `ended`; `billedAdvance`
lists the months already billed in advance.

```ts title="Signature"
iam.api.billing.listSubscriptions(
  credential: CredentialInput,
  input: { tenantId: string; includeEnded?: boolean },
): Promise<SubscriptionView[]>
```

## listUsage [#listusage]

Lists raw usage events recorded in the tenant for a month, newest first.

**HTTP:** `POST /api/iam/billing/listUsage` (requires a credential) · **Browser client:** `client.billing.listUsage()`

* **Permission:** `iam:billing:read` on `iam/billing`.
* **Audited as:** `iam:billing:read`.

Only the tenant itself (not its subtree), at most `limit` (1 to 500, default 100) events, optionally for one `meter`
or identity. Events are kept `usageRetentionDays` after their month; reports use daily roll-ups and outlive them.

```ts title="Signature"
iam.api.billing.listUsage(
  credential: CredentialInput,
  input: {
    tenantId: string;
    period?: string;
    meter?: string;
    identityId?: string;
    limit?: number;
  },
): Promise<{
  tenantId: string;
  period: string;
  events: {
    idempotencyKey?: string | undefined;
    meterId: string;
    meter: string;
    quantity: number;
    costMicros?: number;
    identityId?: string;
    agentId?: string;
    teamIds: string[];
    departmentId?: string;
    tags?: Record<string, string>;
    occurredAt: number;
    period: string;
    day: string;
    recordedAt: number;
    recordedBy: string;
    sourceId?: string;
    id: string;
    tenantId: string;
  }[];
}>
```

## markPaid [#markpaid]

Marks a finalized or uncollectible statement paid by recording a `manual` payment of the amount due, with an optional reference.

**HTTP:** `POST /api/iam/billing/markPaid` (requires a credential) · **Browser client:** `client.billing.markPaid()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`), called on the account
  or the root tenant.
* **Audited as:** `billing:statement-paid`.
* **Errors:** `INVALID_TRANSITION` (409) for a draft, paid or void statement.

```ts title="Signature"
iam.api.billing.markPaid(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string; reference?: string },
): Promise<StatementSummary>
```

## markUncollectible [#markuncollectible]

Writes a finalized invoice off as uncollectible; a later payment still settles it.

**HTTP:** `POST /api/iam/billing/markUncollectible` (requires a credential) · **Browser client:** `client.billing.markUncollectible()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`).
* **Audited as:** `billing:statement-uncollectible`.
* **Errors:** `INVALID_TRANSITION` (409) for an invoice that is not finalized.

Uncollectible invoices get no payment reminders.

```ts title="Signature"
iam.api.billing.markUncollectible(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string },
): Promise<StatementSummary>
```

## mySpend [#myspend]

Reports the caller's own spend: their usage and that of the agents they sponsor, with the budgets set on them.

**HTTP:** `POST /api/iam/billing/mySpend` (requires a credential) · **Browser client:** `client.billing.mySpend()`

* **Permission:** The caller's own session in their own tenant.
* **Audited as:** not audited.
* **Errors:** `ACCESS_DENIED` (403) for another tenant; `INVALID_INPUT` for a `groupBy` other than `meter`, `day`,
  `agent`, `tenant` or `tag:{name}`.

```ts title="Signature"
iam.api.billing.mySpend(
  credential: CredentialInput,
  input: { tenantId: string; period?: string; groupBy?: SpendGroupBy },
): Promise<SpendReport & { budgets: BudgetStatus[] }>
```

## previewStatement [#previewstatement]

Builds the statement a billing account would receive for a month (the current one so far by default), without issuing it.

**HTTP:** `POST /api/iam/billing/previewStatement` (requires a credential) · **Browser client:** `client.billing.previewStatement()`

* **Permission:** `iam:billing:read` on `iam/billing/statements`.
* **Audited as:** `iam:billing:read`.
* **Errors:** `INVALID_INPUT` when the tenant is not a billing account.

```ts title="Signature"
iam.api.billing.previewStatement(
  credential: CredentialInput,
  input: { tenantId: string; period?: string },
): Promise<StatementDraft>
```

## quote [#quote]

Prices a quantity of a meter as a month total for the tenant's billing account.

**HTTP:** `POST /api/iam/billing/quote` (requires a credential) · **Browser client:** `client.billing.quote()`

* **Permission:** `iam:billing:read` on `iam/billing/meters/{key}`.
* **Audited as:** `iam:billing:read`.
* **Errors:** `NOT_FOUND` for an unknown meter; `INVALID_INPUT` for a reported meter or a negative quantity.

Tiers and free units apply as they would to the account's month total; `unpriced` is true without a price.

```ts title="Signature"
iam.api.billing.quote(
  credential: CredentialInput,
  input: { tenantId: string; meter: string; quantity: number; period?: string },
): Promise<| {
      price: PriceView;
      meter: string;
      quantity: number;
      period: string;
      currency: string;
      amountMicros: number;
      amount: number;
    }
  | {
      unpriced: true;
      meter: string;
      quantity: number;
      period: string;
      currency: string;
      amountMicros: number;
      amount: number;
    }>
```

## record [#record]

Records usage of a meter in the tenant, attributed to an identity, their teams and department, with optional tags.

**HTTP:** `POST /api/iam/billing/record` (requires a credential) · **Browser client:** `client.billing.record()`

* **Permission:** `iam:billing:record` on `iam/billing/meters/{key}`.
* **Audited as:** `iam:billing:record`.
* **Errors:** `NOT_FOUND` for an unknown meter or an identity outside the tenant and its ancestors; `METER_ARCHIVED`
  (409); `BILLING_PERIOD_CLOSED` (409) for a month already invoiced; `SPEND_LIMIT_REACHED` (402) with
  `enforceBudgets` under a spent enforced budget; `CONFLICT` (409) for an idempotency key used on another meter;
  `INVALID_INPUT` for a negative quantity, `cost` on a rate-card meter or none on a reported one, or `occurredAt` more
  than five minutes ahead or a year back.

`idempotencyKey` makes retries safe: a repeat returns the first receipt with `duplicate: true`. `teamId` attributes the
usage to one team instead of the person's own. Server code records without an audit event per call through
`iam.billing.record`, the usual choice for metering.

```ts
await client.billing.record({
  tenantId,
  meter: 'api-calls',
  quantity: 1,
  identityId,
  tags: { endpoint: 'search' },
  idempotencyKey: requestId,
});
```

```ts title="Signature"
iam.api.billing.record(
  credential: CredentialInput,
  input: UsageInput & { enforceBudgets?: boolean },
): Promise<UsageReceipt>
```

## recordMany [#recordmany]

Records up to 100 usage events in one transaction, all or nothing.

**HTTP:** `POST /api/iam/billing/recordMany` (requires a credential) · **Browser client:** `client.billing.recordMany()`

* **Permission:** `iam:billing:record` on `iam/billing` in the call's tenant.
* **Audited as:** `iam:billing:record`.
* **Errors:** as `record`; `INVALID_INPUT` for an event outside the tenant's subtree or more than 100 events.

```ts title="Signature"
iam.api.billing.recordMany(
  credential: CredentialInput,
  input: {
    tenantId: string;
    events: Omit<UsageInput, 'tenantId'> & { tenantId?: string }[];
  },
): Promise<{ receipts: UsageReceipt[] }>
```

## recordPayment [#recordpayment]

Records a payment against a finalized or uncollectible invoice.

**HTTP:** `POST /api/iam/billing/recordPayment` (requires a credential) · **Browser client:** `client.billing.recordPayment()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`), called on the account
  or the root tenant.
* **Audited as:** `billing:payment`.
* **Errors:** `INVALID_TRANSITION` (409) for a draft, paid or void invoice; `INVALID_INPUT` for an amount of 0 or a
  `receivedAt` in the future.

`amount` (currency units) defaults to the amount due; `method` (default `manual`) and `reference` describe it. A
partial payment leaves the rest due; once payments and credit notes cover the invoice it is `paid`. A payment above
the amount due keeps the excess as account credit (`payment.overpaymentMicros`, `payment.creditId`). Payment
processors report payments through `iam.billing.recordPayment({ statementId | number, amount, idempotencyKey })`
instead, where the idempotency key makes webhook redelivery safe.

```ts
await iam.api.billing.recordPayment(root, {
  tenantId: acmeId,
  statementId,
  amount: 300,
  method: 'bank_transfer',
  reference: 'wire-88213',
});
```

```ts title="Signature"
iam.api.billing.recordPayment(
  credential: CredentialInput,
  input: {
    tenantId: string;
    statementId: string;
    amount?: number;
    method?: string;
    reference?: string;
    receivedAt?: number;
  },
): Promise<{ statement: StatementSummary; payment: InvoicePayment }>
```

## redeemCoupon [#redeemcoupon]

Redeems a coupon code for the billing account.

**HTTP:** `POST /api/iam/billing/redeemCoupon` (requires a credential) · **Browser client:** `client.billing.redeemCoupon()`

* **Permission:** `iam:billing:manage` on `iam/billing/discounts` in the account.
* **Audited as:** `billing:coupon-redeem`.
* **Errors:** `CONFLICT` (409) when the account already redeemed the coupon; `NOT_FOUND` for a code that is unknown,
  inactive, past `redeemBy` or out of redemptions (all answer the same); `INVALID_INPUT` when the tenant is not a
  billing account.

The resulting discount applies to the account's invoices after the contract discount, in the order codes were
redeemed. Voiding an invoice gives a one-off discount back.

```ts title="Signature"
iam.api.billing.redeemCoupon(
  credential: CredentialInput,
  input: { tenantId: string; code: string },
): Promise<DiscountView>
```

## removeDiscount [#removediscount]

Ends a billing account's discount now.

**HTTP:** `POST /api/iam/billing/removeDiscount` (requires a credential) · **Browser client:** `client.billing.removeDiscount()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/discounts`), called on the account.
* **Audited as:** `billing:discount-remove`.
* **Errors:** `INVALID_TRANSITION` (409) for a discount that has already ended.

```ts title="Signature"
iam.api.billing.removeDiscount(
  credential: CredentialInput,
  input: { tenantId: string; discountId: string },
): Promise<DiscountView>
```

## renderInvoice [#renderinvoice]

Returns an invoice as a standalone HTML page to print or save as PDF.

**HTTP:** `POST /api/iam/billing/renderInvoice` (requires a credential) · **Browser client:** `client.billing.renderInvoice()`

* **Permission:** `iam:billing:read` on `iam/billing/statements`.
* **Audited as:** `iam:billing:read`.
* **Errors:** `NOT_FOUND` for an invoice outside the tenant's subtree.

The result is `{ filename, contentType, body }`. The page names the issuer (the `billing.issuer` option: name,
address, tax ID, contact, and `paymentInstructions` under the totals), the bill-to details, every line with its tier
sub-lines, service period and proration, then discounts, coupons, credit, tax, payments, credit notes and the amount
due. It has no scripts and only inline styles, so it can be served with a strict content security policy.

```ts title="Signature"
iam.api.billing.renderInvoice(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string },
): Promise<InvoiceDocument>
```

## resumeSubscription [#resumesubscription]

Undoes a cancellation at the end of the month before it takes effect.

**HTTP:** `POST /api/iam/billing/resumeSubscription` (requires a credential) · **Browser client:** `client.billing.resumeSubscription()`

* **Permission:** `iam:billing:manage` on `iam/billing/subscriptions` (self-serve plans), or a root administrator.
* **Audited as:** `billing:subscription-resume`.
* **Errors:** `INVALID_TRANSITION` (409) for a subscription that is not set to cancel, or has ended.

```ts title="Signature"
iam.api.billing.resumeSubscription(
  credential: CredentialInput,
  input: { tenantId: string; subscriptionId: string },
): Promise<SubscriptionView>
```

## revokeCredit [#revokecredit]

Withdraws what is left of a credit.

**HTTP:** `POST /api/iam/billing/revokeCredit` (requires a credential) · **Browser client:** `client.billing.revokeCredit()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/credits`).
* **Audited as:** `billing:credit-revoke`.
* **Errors:** `INVALID_TRANSITION` (409) for a credit already revoked.

```ts title="Signature"
iam.api.billing.revokeCredit(
  credential: CredentialInput,
  input: { tenantId: string; creditId: string },
): Promise<CreditView>
```

## setPrice [#setprice]

Sets or removes a rate-card price for a meter the tenant defines: its list price, or a negotiated price for a tenant below it.

**HTTP:** `POST /api/iam/billing/setPrice` (requires a credential) · **Browser client:** `client.billing.setPrice()`

* **Permission:** `iam:billing:manage` on `iam/billing/meters/{key}` in the defining tenant.
* **Audited as:** `billing:price`.
* **Errors:** `NOT_FOUND` when the tenant does not define the meter; `INVALID_INPUT` for a reported meter, a target
  outside the subtree, malformed tiers, or `effectiveFrom` more than 12 months back; `BILLING_PERIOD_CLOSED` (409) when
  a month from `effectiveFrom` on is already invoiced for the tenants it reaches.

`price` takes currency units: `per-unit` (`unitAmount`), `graduated` or `volume` (`tiers` of `upTo` and `unitAmount`,
optional `flatAmount`, the last `upTo: null`), or `package` (`packageSize`, `packageAmount`), each with an optional
`includedQuantity`. `effectiveFrom` (default this month) starts the price; `price: null` removes that entry.

```ts
await iam.api.billing.setPrice(root, {
  tenantId: rootTenantId,
  meter: 'api-calls',
  targetTenantId: acmeId,
  price: { model: 'per-unit', unitAmount: 0.0003 },
  note: 'Enterprise agreement',
});
```

```ts title="Signature"
iam.api.billing.setPrice(
  credential: CredentialInput,
  input: {
    tenantId: string;
    meter: string;
    targetTenantId?: string;
    effectiveFrom?: string;
    price: Record<string, unknown> | null;
    note?: string;
  },
): Promise<PriceView | null>
```

## setProfile [#setprofile]

Creates or updates a billing profile: company, billing emails, tax ID, address, purchase order, cost center, payment terms.

**HTTP:** `POST /api/iam/billing/setProfile` (requires a credential) · **Browser client:** `client.billing.setProfile()`

* **Permission:** `iam:billing:manage` on `iam/billing/profile`; a new profile below an organization is created from an
  ancestor with `targetTenantId`.
* **Audited as:** `billing:profile`.
* **Errors:** `ACCESS_DENIED` (403) when a tenant below an organization creates its own; `INVALID_INPUT` on the root
  tenant or for more than 10 emails.

A profile below an organization makes that tenant a billing account of its own, a decision for its parent; afterwards
the tenant's own billing managers may keep it up to date. `null` clears a field.

```ts title="Signature"
iam.api.billing.setProfile(
  credential: CredentialInput,
  input: {
    tenantId: string;
    targetTenantId?: string;
    companyName?: string | null;
    billingEmails?: string[];
    taxId?: string | null;
    address?: string | null;
    purchaseOrder?: string | null;
    costCenter?: string | null;
    paymentTermsDays?: number | null;
  },
): Promise<ProfileView>
```

## setTerms [#setterms]

Sets a billing account's contract terms: a discount off the subtotal, a minimum monthly commitment, and the tax invoices add.

**HTTP:** `POST /api/iam/billing/setTerms` (requires a credential) · **Browser client:** `client.billing.setTerms()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/terms`), called on the account.
* **Audited as:** `billing:terms`.
* **Errors:** `INVALID_INPUT` when the tenant is not a billing account, for a percentage outside 0 to 100, or a
  negative commitment.

Invoices issued from then on take the subtotal of their lines, subtract `discountPercent`, add the shortfall below
`minimumCommitment` (currency units per month, monthly invoices only) as a true-up, subtract coupons and credit, and
add `taxRatePercent` (labelled `taxLabel`, `Tax` by default) on the rest. `null` clears a term. Spend reports stay at
rate-card prices.

```ts
await iam.api.billing.setTerms(root, {
  tenantId: acmeId,
  discountPercent: 10,
  minimumCommitment: 1000,
  taxRatePercent: 20,
  taxLabel: 'VAT',
});
```

```ts title="Signature"
iam.api.billing.setTerms(
  credential: CredentialInput,
  input: {
    tenantId: string;
    discountPercent?: number | null;
    minimumCommitment?: number | null;
    taxRatePercent?: number | null;
    taxLabel?: string | null;
  },
): Promise<TermsView>
```

## spend [#spend]

Reports the spend of the tenant and every tenant below it for a month, grouped and filtered.

**HTTP:** `POST /api/iam/billing/spend` (requires a credential) · **Browser client:** `client.billing.spend()`

* **Permission:** `iam:billing:read` on `iam/billing`.
* **Audited as:** `iam:billing:read`.
* **Errors:** `NOT_FOUND` for a team, department or sub-tenant outside the scope; `INVALID_INPUT` for an unknown
  `groupBy` or a malformed month.

`groupBy` is `meter` (default), `identity`, `agent`, `team`, `department`, `tenant`, `day` or `tag:{name}`. Filters:
`meter`, `identityId`, `teamId`, `departmentId`, `subTenantId`, `rollUp: false` (no sub-teams or sub-departments) and
`billableOnly` (leave out chargeback meters). Each row has `costMicros`, `amount`, `share` (percent) and `quantities`
per meter; the current month carries a linear `forecast`. For showback, `shareUnattributed: true` (grouped by
`identity`, `agent`, `team` or `department`) spreads the unattributed row over the other groups by their share of
spend, reported as `sharedMicros` per row and for the report.

```ts
const report = await iam.api.billing.spend(admin, { tenantId, groupBy: 'team' });
```

```ts title="Signature"
iam.api.billing.spend(
  credential: CredentialInput,
  input: { tenantId: string; period?: string; groupBy?: SpendGroupBy } & SpendFilters,
): Promise<SpendReport>
```

## subscribe [#subscribe]

Subscribes the billing account to a plan (id or key).

**HTTP:** `POST /api/iam/billing/subscribe` (requires a credential) · **Browser client:** `client.billing.subscribe()`

* **Permission:** `iam:billing:manage` on `iam/billing/subscriptions`: self-serve plans for the account's billing
  managers, any plan for root administrators.
* **Audited as:** `billing:subscription`.
* **Errors:** `ACCESS_DENIED` (403) for a plan that is not self-serve, or `trialDays` from anyone but a root
  administrator; `CONFLICT` (409) when the account already subscribes to the plan; `INVALID_TRANSITION` (409) for an
  archived plan; `INVALID_INPUT` when the tenant is not a billing account.

`seats` defaults to 1. Outside a trial the rest of this month's advance fees and seats are invoiced at once (the
result's `invoice`, `billingReason: 'subscription'`); after that each monthly invoice bills the month ahead. A trial
bills nothing until it ends; the part of the month after it is billed on that month's invoice.

```ts
const { subscription, invoice } = await iam.api.billing.subscribe(orgAdmin, {
  tenantId: acmeId,
  plan: 'team',
  seats: 8,
});
```

```ts title="Signature"
iam.api.billing.subscribe(
  credential: CredentialInput,
  input: { tenantId: string; plan: string; seats?: number; trialDays?: number },
): Promise<{ subscription: SubscriptionView; invoice?: StatementSummary }>
```

## teamSpend [#teamspend]

Reports a team's spend, with the teams below it, grouped by `identity` by default.

**HTTP:** `POST /api/iam/billing/teamSpend` (requires a credential) · **Browser client:** `client.billing.teamSpend()`

* **Permission:** The team's maintainers (and those of teams above it), or `iam:billing:read`.
* **Audited as:** `iam:billing:read`, with `metadata.via` `team-maintainer` or `permission`.
* **Errors:** `ACCESS_DENIED` (403) for anyone else; `NOT_FOUND` for a team outside the tenant.

```ts title="Signature"
iam.api.billing.teamSpend(
  credential: CredentialInput,
  input: { tenantId: string; teamId: string; period?: string; groupBy?: SpendGroupBy },
): Promise<SpendReport>
```

## trend [#trend]

Returns monthly totals for the last `months` months (1 to 24, default 6), with the filters of `spend`.

**HTTP:** `POST /api/iam/billing/trend` (requires a credential) · **Browser client:** `client.billing.trend()`

* **Permission:** `iam:billing:read` on `iam/billing`.
* **Audited as:** `iam:billing:read`.

```ts title="Signature"
iam.api.billing.trend(
  credential: CredentialInput,
  input: { tenantId: string; months?: number } & SpendFilters,
): Promise<SpendTrend>
```

## updateBudget [#updatebudget]

Changes a budget's name, amount, period, meters, thresholds, alerts or enforcement; its subject stays.

**HTTP:** `POST /api/iam/billing/updateBudget` (requires a credential) · **Browser client:** `client.billing.updateBudget()`

* **Permission:** `iam:billing:manage` on `iam/billing/budgets`.
* **Audited as:** `billing:budget-update`.
* **Errors:** `CONFLICT` (409) for a name another budget uses; `NOT_FOUND` for a budget of another tenant.

`meters: null` counts every meter again.

```ts title="Signature"
iam.api.billing.updateBudget(
  credential: CredentialInput,
  input: { tenantId: string; budgetId: string } & Partial<
    Omit<BillingBudgetInput, 'subjectType' | 'subjectId' | 'meters'>
  > & { meters?: string[] | null },
): Promise<BillingBudgetView>
```

## updateMeter [#updatemeter]

Renames a meter, changes its unit or description, or archives it.

**HTTP:** `POST /api/iam/billing/updateMeter` (requires a credential) · **Browser client:** `client.billing.updateMeter()`

* **Permission:** `iam:billing:manage` on `iam/billing/meters/{key}`.
* **Audited as:** `billing:meter-update`.
* **Errors:** `NOT_FOUND` when the tenant does not define the key; `INVALID_INPUT` when changing `aggregation` or
  `pricing`.

An archived meter refuses new usage (`METER_ARCHIVED`) and keeps its history; `archived: false` restores it.

```ts title="Signature"
iam.api.billing.updateMeter(
  credential: CredentialInput,
  input: {
    tenantId: string;
    key: string;
    name?: string;
    unit?: string;
    description?: string | null;
    archived?: boolean;
  },
): Promise<MeterView>
```

## updatePlan [#updateplan]

Changes a plan, by id or key: name, items, description, trial, self-serve, archived.

**HTTP:** `POST /api/iam/billing/updatePlan` (requires a credential) · **Browser client:** `client.billing.updatePlan()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/plans`), called on the root tenant.
* **Audited as:** `billing:plan-update`.
* **Errors:** `NOT_FOUND` for an unknown plan; `INVALID_INPUT` as for `createPlan`.

`null` clears `description` or `trialDays`. An archived plan takes no new subscriptions. Changes apply to invoices
drawn up afterwards.

```ts title="Signature"
iam.api.billing.updatePlan(
  credential: CredentialInput,
  input: {
    tenantId: string;
    plan: string;
    name?: string;
    items?: unknown[];
    description?: string | null;
    trialDays?: number | null;
    selfServe?: boolean;
    archived?: boolean;
  },
): Promise<PlanView>
```

## updateSubscription [#updatesubscription]

Changes a subscription's seats.

**HTTP:** `POST /api/iam/billing/updateSubscription` (requires a credential) · **Browser client:** `client.billing.updateSubscription()`

* **Permission:** `iam:billing:manage` on `iam/billing/subscriptions` (self-serve plans), or a root administrator.
* **Audited as:** `billing:subscription-update`.
* **Errors:** `INVALID_TRANSITION` (409) for an ended subscription; `INVALID_INPUT` for seats outside 0 to 1000000.

Seats billed in advance for this month are prorated as invoice items on the next invoice (`invoiceItems`): added seats
for the rest of the month, credit for removed ones. Arrears seats follow the seat history on their own.

```ts title="Signature"
iam.api.billing.updateSubscription(
  credential: CredentialInput,
  input: { tenantId: string; subscriptionId: string; seats: number },
): Promise<{ subscription: SubscriptionView; invoiceItems: InvoiceItemView[] }>
```

## voidStatement [#voidstatement]

Voids a finalized statement: its credit, invoice items, coupons and advance-billed months come back and the month reopens.

**HTTP:** `POST /api/iam/billing/voidStatement` (requires a credential) · **Browser client:** `client.billing.voidStatement()`

* **Permission:** Root administrators only (`iam:billing:manage` on `iam/billing/statements`), called on the account
  or the root tenant.
* **Audited as:** `billing:statement-void`.
* **Errors:** `INVALID_TRANSITION` (409) for a statement already void, a draft, or one with payments or credit notes
  (issue a credit note instead).

Fix the usage, then close the month again: the new statement gets a new number. Credit the invoice created from a
negative balance is revoked.

```ts title="Signature"
iam.api.billing.voidStatement(
  credential: CredentialInput,
  input: { tenantId: string; statementId: string; reason: string },
): Promise<StatementSummary>
```
