Billable metrics
The named things you charge for.
A billable metric 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 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-metricsA metering rule can make a plain billable metric on its own. List here to see everything, not only what you made by hand.
List billable metrics
/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, oldest first.
| Field | Type | Meaning |
|---|---|---|
cursoroptional | string | Opaque pagination cursor from the previous page's |
limitoptional | integer | Page size. Default 20, max 100. |
{
"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
}
} | Field | Type | Meaning |
|---|---|---|
datarequired | array | One row per billable metric, oldest first. |
paginationrequired | object | Cursor-based pagination envelope. Returned on every list endpoint. No |
See also
Create a billable metric
/v1/billable-metrics | Idempotency-Key | Not required for this operation. |
|---|---|
| Environment | Sandbox and Live |
| Auth | Tenant API key |
| Writes | One new billable metric. status is active right away. |
| Fires | No webhooks. |
Creating a billable metric 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 for that.
| Field | Type | Meaning |
|---|---|---|
keyrequired | string | The name your code sends on every usage event and metering rule for this metric, like If you leave it out: QuotaStack rejects the call. This field is required. |
namerequired | string | A label for lists and dashboards. If you leave it out: QuotaStack rejects the call. This field is required. |
descriptionoptional | string | Longer, free text for dashboards. If you leave it out: Left out, QuotaStack stores an empty string. |
typeoptional | string | What kind of entitlement this metric is, and what a check call returns for it. If you leave it out: Left out, this metric is
|
default_valueoptional | object | The starting value for this metric’s If you leave it out: Left out, QuotaStack stores |
metadataoptional | object | Your own key-value tags. QuotaStack stores them unchanged and returns them on every read. If you leave it out: Left out, QuotaStack stores |
{
"key": "chat_message",
"name": "Chat message",
"description": "One message sent in a chat session.",
"type": "metered"
} {
"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"
} | Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | |
tenant_idrequired | string (uuid) | |
keyrequired | string | The name your code sends with each use of this metric, and with its metering rule. |
namerequired | string | |
descriptionrequired | string | Longer, free text for dashboards. An empty string unless you set one. |
statusrequired | string | Active or inactive, for your own records. QuotaStack does not use this to block usage today.
|
typerequired | string | What kind of entitlement this metric is, and what a check call returns for it. Fixed after creation.
|
default_valuerequired | object | The starting value for this metric’s If you leave it out: Left out, QuotaStack stores |
metadatarequired | object | Your own key-value tags, unchanged since the last write. |
created_atrequired | string (date-time) | |
updated_atrequired | string (date-time) |
Errors
409— Thiskeyalready exists for your tenant. A metering rule made earlier for this key can cause this too. Creating a rule for an unknown key makes a plain billable metric on its own. CallgetBillableMetricto see what is already there. conflict422—keyornameis missing,typeis not one of the four values, ordefault_valuedoes not matchtype’s shape. A missingIdempotency-Keyheader returns this too. The response names the field where it can. validation-error
Retrieve a billable metric
/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 by its key. The metering rule that prices it is separate — list those, filtered by this same key.
| Field | Type | Meaning |
|---|---|---|
keyrequired | string | The billable metric’s own key, exactly as you sent it to |
{
"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"
} | Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | |
tenant_idrequired | string (uuid) | |
keyrequired | string | The name your code sends with each use of this metric, and with its metering rule. |
namerequired | string | |
descriptionrequired | string | Longer, free text for dashboards. An empty string unless you set one. |
statusrequired | string | Active or inactive, for your own records. QuotaStack does not use this to block usage today.
|
typerequired | string | What kind of entitlement this metric is, and what a check call returns for it. Fixed after creation.
|
default_valuerequired | object | The starting value for this metric’s If you leave it out: Left out, QuotaStack stores |
metadatarequired | object | Your own key-value tags, unchanged since the last write. |
created_atrequired | string (date-time) | |
updated_atrequired | string (date-time) |
Errors
404— No billable metric here has thiskey. not-found
See also
Update a billable metric
/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, 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.
| Field | Type | Meaning |
|---|---|---|
keyrequired | string | The billable metric’s own key, exactly as you sent it to |
| Field | Type | Meaning |
|---|---|---|
nameoptional | string | A label for lists and dashboards. If you leave it out: The old value stays. QuotaStack changes nothing. |
descriptionoptional | string | Longer, free text for dashboards. Send an empty string to clear it. If you leave it out: The old value stays. QuotaStack changes nothing. |
statusoptional | string | 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.
|
typeoptional | string | Must equal the metric’s current If you leave it out: The old value stays. QuotaStack changes nothing.
|
default_valueoptional | object | The starting value for this metric’s If you leave it out: The old value stays. QuotaStack changes nothing. Sending a value REPLACES the stored object outright — it does not merge. |
metadataoptional | object | 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. |
{
"name": "Chat message (v2)"
} {
"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"
} | Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | |
tenant_idrequired | string (uuid) | |
keyrequired | string | The name your code sends with each use of this metric, and with its metering rule. |
namerequired | string | |
descriptionrequired | string | Longer, free text for dashboards. An empty string unless you set one. |
statusrequired | string | Active or inactive, for your own records. QuotaStack does not use this to block usage today.
|
typerequired | string | What kind of entitlement this metric is, and what a check call returns for it. Fixed after creation.
|
default_valuerequired | object | The starting value for this metric’s If you leave it out: Left out, QuotaStack stores |
metadatarequired | object | Your own key-value tags, unchanged since the last write. |
created_atrequired | string (date-time) | |
updated_atrequired | string (date-time) |
Errors
404— No billable metric here has thiskey. not-found422—typedoes not match the metric’s current type,statusis notactiveorinactive, ordefault_valuedoes not matchtype’s shape. A missingIdempotency-Keyheader returns this too. Sending the metric’s own currenttypeback is fine and changes nothing. validation-error
See also
Loading…