---
title: "Billable metrics"
description: "The named things you charge for — the key your code sends every time a customer uses one."
---

# Billable metrics

The named things you charge for.

A [billable metric](/docs/concepts/metering) is the named thing you charge
for. One chat message, one API call, one gigabyte of storage — each is a
metric with its own key.

A metric alone has no price. A [metering rule](/docs/concepts/metering) sets
the price for one unit, and a usage event reports that a customer used it.

## Which call do I want?

- You are defining a new thing to charge for or gate → `POST /v1/billable-metrics`
- You already know the key and want its definition → `GET /v1/billable-metrics/{key}`
- You want every billable metric you have defined → `GET /v1/billable-metrics`
  A [metering rule](/docs/concepts/metering) can make a plain [billable metric](/docs/concepts/metering) on its own. List here to see everything, not only what you made by hand.

4 of 4 operations documented in full.

## List billable metrics

`GET /v1/billable-metrics`

- **Idempotency-Key:** Not used. This is a read.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Nothing. A read has no side effects.
- **Fires:** No webhooks.

This page lists [billable metrics](/docs/concepts/metering), oldest first.

### Query parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `cursor` | string | optional | Opaque pagination cursor from the previous page's `pagination.next_cursor`. Omit to start at the first page. |
| `limit` | integer | optional | Page size. Default 20, max 100. |

### Response 200

```json
{
  "data": [
    {
      "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a52",
      "key": "chat_message",
      "name": "Chat message",
      "description": "One message sent in a chat session.",
      "status": "active",
      "type": "metered",
      "default_value": {},
      "metadata": {},
      "created_at": "2026-07-20T09:00:00Z",
      "updated_at": "2026-07-20T09:00:00Z"
    }
  ],
  "pagination": {
    "has_more": false,
    "next_cursor": null
  }
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `data` | array | required | One row per [billable metric](/docs/concepts/metering), oldest first. |
| `pagination` | object | required | Cursor-based pagination envelope. Returned on every list endpoint. No `total` — cursor-only. |

### See also

- [Metering](/docs/concepts/metering)

## Create a billable metric

`POST /v1/billable-metrics`

- **Idempotency-Key:** Not required for this operation.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** One new [billable metric](/docs/concepts/metering). `status` is `active` right away.
- **Fires:** No webhooks.

Creating a [billable metric](/docs/concepts/metering) names one thing your app meters or gates. Creating one needs an `Idempotency-Key` header, like every write. openapi.yaml skips it here, but QuotaStack still checks. The metric alone sets no price. Add a [metering rule](/docs/concepts/metering) for that.

### Body parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `key` | string | required | The name your code sends on every usage event and [metering rule](/docs/concepts/metering) for this metric, like `chat_message`. Must be unique for your tenant. If you leave it out: QuotaStack rejects the call. This field is required. |
| `name` | string | required | A label for lists and dashboards. If you leave it out: QuotaStack rejects the call. This field is required. |
| `description` | string | optional | Longer, free text for dashboards. If you leave it out: Left out, QuotaStack stores an empty string. |
| `type` | string | optional | What kind of [entitlement](/docs/concepts/entitlements) this metric is, and what a check call returns for it. If you leave it out: Left out, this metric is `metered`. `metered` — Priced by a [metering rule](/docs/concepts/metering) and paid for out of the customer’s balance. A check returns the balance and the estimated cost. `boolean` — A plain on/off flag, like SSO access. A check returns `{"enabled": true}` or `{"enabled": false}`, and costs nothing. `gauge` — A count with a cap, like a seat limit. A check returns the cap and how many the customer has used so far. `static` — A JSON value your app reads directly, like a list of models you allow. A check returns that value only. |
| `default_value` | object | optional | The starting value for this metric’s `type`. `metered` ignores this field entirely. `boolean` takes `{"enabled": <bool>}`. `gauge` takes `{"cap": <non-negative integer>}`. `static` takes any object, commonly `{"config": ...}`. If you leave it out: Left out, QuotaStack stores `{}`. |
| `metadata` | object | optional | Your own key-value tags. QuotaStack stores them unchanged and returns them on every read. If you leave it out: Left out, QuotaStack stores `{}`. |

### Request

```json
{
  "key": "chat_message",
  "name": "Chat message",
  "description": "One message sent in a chat session.",
  "type": "metered"
}
```

### Response 201

```json
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a52",
  "key": "chat_message",
  "name": "Chat message",
  "description": "One message sent in a chat session.",
  "status": "active",
  "type": "metered",
  "default_value": {},
  "metadata": {},
  "created_at": "2026-07-20T09:00:00Z",
  "updated_at": "2026-07-20T09:00:00Z"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required |  |
| `tenant_id` | string (uuid) | required |  |
| `key` | string | required | The name your code sends with each use of this metric, and with its [metering rule](/docs/concepts/metering). |
| `name` | string | required |  |
| `description` | string | required | Longer, free text for dashboards. An empty string unless you set one. |
| `status` | string | required | Active or inactive, for your own records. QuotaStack does not use this to block usage today. `active` — The normal state for a metric in use. `inactive` — A label only — checks and usage still work the same way. |
| `type` | string | required | What kind of [entitlement](/docs/concepts/entitlements) this metric is, and what a check call returns for it. Fixed after creation. `metered` — Priced by a [metering rule](/docs/concepts/metering) and paid for out of the customer’s balance. A check returns the balance and the estimated cost. `boolean` — A plain on/off flag, like SSO access. A check returns `{"enabled": true}` or `{"enabled": false}`, and costs nothing. `gauge` — A count with a cap, like a seat limit. A check returns the cap and how many the customer has used so far. `static` — A JSON value your app reads directly, like a list of models you allow. A check returns that value only. |
| `default_value` | object | required | The starting value for this metric’s `type`. `metered` ignores this field entirely. `boolean` takes `{"enabled": <bool>}`. `gauge` takes `{"cap": <non-negative integer>}`. `static` takes any object, commonly `{"config": ...}`. If you leave it out: Left out, QuotaStack stores `{}`. |
| `metadata` | object | required | Your own key-value tags, unchanged since the last write. |
| `created_at` | string (date-time) | required |  |
| `updated_at` | string (date-time) | required |  |

### Errors

- `409` — This `key` already exists for your tenant. A [metering rule](/docs/concepts/metering) made earlier for this key can cause this too. Creating a rule for an unknown key makes a plain [billable metric](/docs/concepts/metering) on its own. Call `getBillableMetric` to see what is already there. See [conflict](/docs/api/errors/conflict).
- `422` — `key` or `name` is missing, `type` is not one of the four values, or `default_value` does not match `type`’s shape. A missing `Idempotency-Key` header returns this too. The response names the field where it can. See [validation-error](/docs/api/errors/validation-error).

### See also

- [Metering](/docs/concepts/metering)
- [Entitlements](/docs/concepts/entitlements)
- [Plan-variant entitlements](/docs/concepts/plan-variant-entitlements)
- [Setting up typed entitlements](/docs/cookbook/entitlement-types-setup)

## Retrieve a billable metric

`GET /v1/billable-metrics/{key}`

- **Idempotency-Key:** Not used. This is a read.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Nothing. A read has no side effects.
- **Fires:** No webhooks.

This endpoint returns one [billable metric](/docs/concepts/metering) by its `key`. The [metering rule](/docs/concepts/metering) that prices it is separate — list those, filtered by this same `key`.

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `key` | string | required | The [billable metric](/docs/concepts/metering)’s own key, exactly as you sent it to `createBillableMetric`. |

### Response 200

```json
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a52",
  "key": "chat_message",
  "name": "Chat message",
  "description": "One message sent in a chat session.",
  "status": "active",
  "type": "metered",
  "default_value": {},
  "metadata": {},
  "created_at": "2026-07-20T09:00:00Z",
  "updated_at": "2026-07-20T09:00:00Z"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required |  |
| `tenant_id` | string (uuid) | required |  |
| `key` | string | required | The name your code sends with each use of this metric, and with its [metering rule](/docs/concepts/metering). |
| `name` | string | required |  |
| `description` | string | required | Longer, free text for dashboards. An empty string unless you set one. |
| `status` | string | required | Active or inactive, for your own records. QuotaStack does not use this to block usage today. `active` — The normal state for a metric in use. `inactive` — A label only — checks and usage still work the same way. |
| `type` | string | required | What kind of [entitlement](/docs/concepts/entitlements) this metric is, and what a check call returns for it. Fixed after creation. `metered` — Priced by a [metering rule](/docs/concepts/metering) and paid for out of the customer’s balance. A check returns the balance and the estimated cost. `boolean` — A plain on/off flag, like SSO access. A check returns `{"enabled": true}` or `{"enabled": false}`, and costs nothing. `gauge` — A count with a cap, like a seat limit. A check returns the cap and how many the customer has used so far. `static` — A JSON value your app reads directly, like a list of models you allow. A check returns that value only. |
| `default_value` | object | required | The starting value for this metric’s `type`. `metered` ignores this field entirely. `boolean` takes `{"enabled": <bool>}`. `gauge` takes `{"cap": <non-negative integer>}`. `static` takes any object, commonly `{"config": ...}`. If you leave it out: Left out, QuotaStack stores `{}`. |
| `metadata` | object | required | Your own key-value tags, unchanged since the last write. |
| `created_at` | string (date-time) | required |  |
| `updated_at` | string (date-time) | required |  |

### Errors

- `404` — No [billable metric](/docs/concepts/metering) here has this `key`. See [not-found](/docs/api/errors/not-found).

### See also

- [Metering](/docs/concepts/metering)

## Update a billable metric

`PATCH /v1/billable-metrics/{key}`

- **Idempotency-Key:** Not required for this operation.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Applies the fields you send. `metadata` and `default_value` REPLACE the stored object outright — unlike a customer update, neither one merges.
- **Fires:** No webhooks.

This endpoint changes one or more fields on an existing [billable metric](/docs/concepts/metering), found by `key`. Updating one needs an `Idempotency-Key` header, like every write. openapi.yaml skips it here, but QuotaStack still checks. A field you skip keeps its old value. `type` cannot change after creation.

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `key` | string | required | The [billable metric](/docs/concepts/metering)’s own key, exactly as you sent it to `createBillableMetric`. |

### Body parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `name` | string | optional | A label for lists and dashboards. If you leave it out: The old value stays. QuotaStack changes nothing. |
| `description` | string | optional | Longer, free text for dashboards. Send an empty string to clear it. If you leave it out: The old value stays. QuotaStack changes nothing. |
| `status` | string | optional | Marks the metric active or inactive, for your own records. QuotaStack does not use this to block usage today. If you leave it out: The old value stays. QuotaStack changes nothing. `active` — The normal state for a metric in use. `inactive` — A label only — checks and usage still work the same way. |
| `type` | string | optional | Must equal the metric’s current `type`. A different value returns `422`. The same value is fine and changes nothing. If you leave it out: The old value stays. QuotaStack changes nothing. `metered` — Priced by a [metering rule](/docs/concepts/metering) and paid for out of the customer’s balance. A check returns the balance and the estimated cost. `boolean` — A plain on/off flag, like SSO access. A check returns `{"enabled": true}` or `{"enabled": false}`, and costs nothing. `gauge` — A count with a cap, like a seat limit. A check returns the cap and how many the customer has used so far. `static` — A JSON value your app reads directly, like a list of models you allow. A check returns that value only. |
| `default_value` | object | optional | The starting value for this metric’s `type`. `metered` ignores this field entirely. `boolean` takes `{"enabled": <bool>}`. `gauge` takes `{"cap": <non-negative integer>}`. `static` takes any object, commonly `{"config": ...}`. If you leave it out: The old value stays. QuotaStack changes nothing. Sending a value REPLACES the stored object outright — it does not merge. |
| `metadata` | object | optional | Your own key-value tags. If you leave it out: The old value stays. QuotaStack changes nothing. Sending a value REPLACES the whole object outright — it does not merge. |

### Request

```json
{
  "name": "Chat message (v2)"
}
```

### Response 200

```json
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a52",
  "key": "chat_message",
  "name": "Chat message (v2)",
  "description": "One message sent in a chat session.",
  "status": "active",
  "type": "metered",
  "default_value": {},
  "metadata": {},
  "created_at": "2026-07-20T09:00:00Z",
  "updated_at": "2026-07-20T11:00:00Z"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required |  |
| `tenant_id` | string (uuid) | required |  |
| `key` | string | required | The name your code sends with each use of this metric, and with its [metering rule](/docs/concepts/metering). |
| `name` | string | required |  |
| `description` | string | required | Longer, free text for dashboards. An empty string unless you set one. |
| `status` | string | required | Active or inactive, for your own records. QuotaStack does not use this to block usage today. `active` — The normal state for a metric in use. `inactive` — A label only — checks and usage still work the same way. |
| `type` | string | required | What kind of [entitlement](/docs/concepts/entitlements) this metric is, and what a check call returns for it. Fixed after creation. `metered` — Priced by a [metering rule](/docs/concepts/metering) and paid for out of the customer’s balance. A check returns the balance and the estimated cost. `boolean` — A plain on/off flag, like SSO access. A check returns `{"enabled": true}` or `{"enabled": false}`, and costs nothing. `gauge` — A count with a cap, like a seat limit. A check returns the cap and how many the customer has used so far. `static` — A JSON value your app reads directly, like a list of models you allow. A check returns that value only. |
| `default_value` | object | required | The starting value for this metric’s `type`. `metered` ignores this field entirely. `boolean` takes `{"enabled": <bool>}`. `gauge` takes `{"cap": <non-negative integer>}`. `static` takes any object, commonly `{"config": ...}`. If you leave it out: Left out, QuotaStack stores `{}`. |
| `metadata` | object | required | Your own key-value tags, unchanged since the last write. |
| `created_at` | string (date-time) | required |  |
| `updated_at` | string (date-time) | required |  |

### Errors

- `404` — No [billable metric](/docs/concepts/metering) here has this `key`. See [not-found](/docs/api/errors/not-found).
- `422` — `type` does not match the metric’s current type, `status` is not `active` or `inactive`, or `default_value` does not match `type`’s shape. A missing `Idempotency-Key` header returns this too. Sending the metric’s own current `type` back is fine and changes nothing. See [validation-error](/docs/api/errors/validation-error).

### See also

- [Metering](/docs/concepts/metering)
