---
title: "Metered domain usage, plan, and quota for the caller's tenant"
method: GET
path: "/billing/usage"
tags: ["Billing"]
---

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

`GET /billing/usage`

## Request

- Base URL: `https://api.customdomain.ai/v1`
- URL: `https://api.customdomain.ai/v1/billing/usage`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer

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

## Query parameters

- `period` string

## Response `200`

Usage

- Usage: Metered usage for ONE calendar month, for the tenant the credential belongs to (never a query parameter). `org`, `period`, `used` and `mode` are always present; the plan block below it is present whenever the org resolves to a billing record, which is every legitimate tenant (a new workspace starts on `starter`). `status`, `has_billing_account` and `billing_state` are billing data: they are sent only to a credential holding `billing:manage` (owners and admins, and a workspace's own non-member API key), never to a member's or viewer's key or a widget token.
  - `org` string, required: The tenant the usage belongs to, derived from the authenticated credential.
  - `period` string, required: UTC calendar month, `YYYY-MM`.
  - `used` integer, required: metered domain connections this period
  - `mode` 'test' | 'live' | '', required: The Stripe key mode the control plane is running against; empty when billing is unconfigured.
  - `plan` 'starter' | 'startup' | 'growth' | 'premium' | 'enterprise' | 'free': `free` is retired: legacy workspaces still carry it until they subscribe, and it cannot be bought or assigned.
  - `plan_name` string: The plan's display name, e.g. `Growth`.
  - `quota` integer: The plan's included domain connections for ONE billing MONTH (`DomainsPerYear` ceiling-divided by 12; `0` = unlimited). Compare `used` against this. `limit` carries the identical value.
  - `limit` integer: 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: `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: 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': The server's classification of `used` against `quota`. `approaching` means at or past 80% of quota; `at_limit` means `used >= quota`. Always `ok` on an unlimited plan (`quota <= 0`).
  - `quota_enforced` boolean: 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: `quota - used`, floored at `0`. Omitted entirely on an unlimited plan, where no remainder is meaningful.
  - `status` string: 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: `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': 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`.

## Other responses

- `401`: Error envelope: {code, title, details}.
- `503`: Error envelope: {code, title, details}.

---

[API](https://skmtc.dev/customdomain/apis/customdomain-control-plane-api.md) · [All operations](https://skmtc.dev/customdomain/apis/customdomain-control-plane-api/llms.txt) · [OpenAPI document](https://skmtc.dev/customdomain/apis/customdomain-control-plane-api/revisions/34d89cb7a7cd?raw)
