# Metered domain usage, plan, and quota for the caller's tenant

> Metered domain usage, plan, and quota for the caller's tenant: CustomDomain™ API reference in the billing group. Covers parameters, request body and responses.

Source: https://docs.customdomain.ai/docs/api-reference/billing/getUsage

## `GET https://api.customdomain.ai/v1/billing/usage`

The tenant is derived from the authenticated credential, never a query parameter. `?period=` defaults to the current UTC calendar month.

### Authentication

`apiKey`, `widgetJWT`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `period` | query | string | no |  |

### Responses

| Status | Description |
| --- | --- |
| `200` | Usage |
| `401` | Error envelope: {code, title, details}. |
| `503` | Error envelope: {code, title, details}. |

### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `org` | string | yes | The tenant the usage belongs to, derived from the authenticated credential. |
| `period` | string | yes | UTC calendar month, `YYYY-MM`. |
| `used` | integer | yes | metered domain connections this period (one calendar month). Compare `quota_used`, not this, with `quota`. |
| `mode` | `test`, `live`, `""` | yes | The Stripe key mode the control plane is running against; empty when billing is unconfigured. |
| `plan` | `starter`, `startup`, `growth`, `premium`, `enterprise`, `free` | no | `free` is retired: legacy workspaces still carry it until they subscribe, and it cannot be bought or assigned. |
| `plan_name` | string | no | The plan's display name, e.g. `Growth`. |
| `quota` | integer | no | The plan's included domain connections over `quota_window_months` calendar months ending with `period`: 10 over 12 months on `starter` (and the retired `free`), the plan's `domains_per_year` divided by 12 and rounded up over one month on every other plan (`0` = unlimited). Compare `quota_used` against this. `limit` carries the identical value. |
| `quota_used` | integer | no | Metered domain connections over the quota window, the count `quota` is compared with. Equal to `used` on a plan whose window is one month. |
| `quota_window_months` | integer | no | How many calendar months `quota` and `quota_used` cover, ending with `period`: 12 on `starter`, 1 on every other plan. |
| `limit` | integer | no | Alias of `quota`, carrying the identical value. This schema declared `limit` while the handler emitted only `quota`; rather than break either reader, both are emitted. Prefer `quota` in new code. |
| `metered` | boolean | no | `true` on tiers priced for overage (`growth`, `enterprise`). It carries NO enforcement meaning; it describes pricing only. Read `hard_cap` for the plan's refusal policy and `quota_enforced` for whether a `402` is possible right now. Never branch on this field. |
| `hard_cap` | boolean | no | Whether this PLAN refuses connections past `quota` (`402 quota_exceeded`) rather than allowing and recording them as overage. `true` only on the entry tiers: `starter` ($10 a month) and the retired `free`. Every larger tier (`startup`, `premium`, `growth`, `enterprise`) is `false` and is never refused at quota. The refusal reaches the tenant's own end users through the embedded widget, so it is worded neutrally for them. Independent of the deployment's `BILLING_ENFORCE_QUOTA`; combine the two via `quota_enforced`. |
| `quota_state` | `ok`, `approaching`, `at_limit` | no | The server's classification of `quota_used` against `quota`. `approaching` means at or past 80% of quota; `at_limit` means `quota_used >= quota`. Always `ok` on an unlimited plan (`quota <= 0`). |
| `quota_enforced` | boolean | no | Whether an over-quota connection would actually be refused with `402 quota_exceeded` right now. True only when the plan's `hard_cap` is set AND the deployment enables enforcement. A client cannot infer the second half, which is why this is served rather than derived. On every paid tier it is `false` at any usage level. A tenant may therefore be `at_limit` with `quota_enforced: false` (over the line with nothing refused), so never treat `at_limit` alone as "blocked". Always `false` on an unlimited plan. |
| `remaining` | integer | no | `quota - quota_used`, floored at `0`. Omitted entirely on an unlimited plan, where no remainder is meaningful. |
| `status` | string | no | Subscription status mirrored from Stripe, e.g. `active`, `past_due` (empty for a workspace that has not subscribed). Owners and admins only. |
| `has_billing_account` | boolean | no | `true` when a Stripe customer exists for the org, i.e. the billing portal is available. Owners and admins only. |
| `billing_state` | `trialing`, `active`, `past_due`, `grace`, `blocked`, `comped`, `canceled`, `legacy_free` | no | A workspace's billing state, evaluated from its record and the clock. `trialing` and `active` are a running Stripe subscription; `past_due` is a failed payment Stripe is still retrying (nothing is refused); `grace` is a legacy Free workspace (or an operator extension) inside its grace period; `legacy_free` is a Free workspace whose grace has not started; `comped` is an operator comp; `blocked` and `canceled` refuse new connections (`402 billing_required`) while live domains keep working. A new workspace (`blocked`, reason `setup_required`) is refused only once its first domain is connected: see `first_domain_free`. |

The full specification is served at `GET https://api.customdomain.ai/v1/openapi.json`.
