---
title: "Entitlements"
description: "Can this customer do this, right now — a fast, cached check your application calls before doing the work."
---

# Entitlements

Can this customer do this, right now?

An [entitlement](/docs/concepts/entitlements) check answers one question: can
this customer do this, right now? You call it before the work starts, not
after.

The answer depends on the customer's plan and their current credit balance. A
check runs fast enough to call on every request.

## Which call do I want?

- You want to know whether a customer can do something, and change nothing → `GET /v1/entitlements/check`
  A check never holds credits. The balance can still change before you call again.
- You want to charge a customer for something they already did → `POST /v1/entitlements/consume`
  A check changes nothing. A consume takes credits. Calling check before consume is not required, and it does not reserve anything. The balance can change between the two calls.
- You already let the action happen, and only want to bill for it afterward → `recordUsage`
  `recordUsage` queues the event and bills it later, in the background. `consumeEntitlement` checks and charges right away, and tells you the result at once. Use `consumeEntitlement` to decide whether to allow the action. Use `recordUsage` once you already have.
- You want every entitlement for a customer, not one metric alone → `GET /v1/customers/{id}/entitlements`
- You are tracking a running count that has a cap, like seats or storage → `POST /v1/customers/{id}/entitlements/{billable_metric_key}/increment`
  A gauge has no price of its own. QuotaStack blocks or allows based on the cap, never the credit balance. Use `decrementGauge` when a seat frees up or storage is deleted.

10 of 10 operations documented in full.

## Check an entitlement

`GET /v1/entitlements/check`

- **Idempotency-Key:** Not used. This is a read.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Nothing to the [entitlement](/docs/concepts/entitlements) itself. A check never touches a balance or a [subscription](/docs/concepts/subscriptions). An unrecognized customer id is different. QuotaStack makes a new customer for it, the same as a credit grant. The new one starts with no balance and no [subscription](/docs/concepts/subscriptions).
- **Fires:** No webhooks.

A check answers one question: can the customer do this, right now? QuotaStack changes nothing when you call it. Send `units` for a [metered](/docs/concepts/metering) metric. QuotaStack ignores `units` for every other type. The four [entitlement](/docs/concepts/entitlements) types answer with different fields — see the type table below.

### Query parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `customer_id` | string | required | The customer’s own id, or your own `external_id`. Either one works here. |
| `billable_metric_key` | string | required | Which [billable metric](/docs/concepts/metering) to check. |
| `units` | integer | optional | How many units to check for. Only a metered metric uses this — every other type ignores it. If you leave it out: Left out, QuotaStack checks for 1 unit. |

### Response 200

```json
{
  "type": "metered",
  "allowed": true,
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "billable_metric_key": "chat_message",
  "units": 1,
  "balance": 250000,
  "reserved_balance": 0,
  "pending_balance": 0,
  "effective_balance": 250000,
  "estimated_cost": 500,
  "balance_after": 249500,
  "subscription_status": "active",
  "overage_policy": "block"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `type` | string | required | Which [entitlement](/docs/concepts/entitlements) type this metric is. `metered` — Priced by a [metering rule](/docs/concepts/metering), paid out of the customer’s balance. `boolean` — A plain on/off flag. Only `allowed` matters — nothing costs credits. `gauge` — A running count with a cap, like a seat limit. Nothing costs credits either. `static` — A JSON value your app reads directly. QuotaStack never charges for reading it. |
| `allowed` | boolean | required | Whether QuotaStack says yes to this call, right now. |
| `customer_id` | string (uuid) | required | The customer’s own id inside QuotaStack. The same id comes back, no matter which id you sent. |
| `billable_metric_key` | string | required | The [billable metric](/docs/concepts/metering) key you asked about. |
| `units` | integer (int64) | optional | How many units you asked about. Only present for a metered check. Unit: a count of billable metric units, not credits. |
| `balance` | integer (int64) | optional | What the customer has, before holds. Only present for a metered check. Unit: millicredits, where 1 credit is 1000 millicredits. |
| `reserved_balance` | integer (int64) | optional | Credits held by an open [reservation](/docs/concepts/reservations). Only present for a metered check. Unit: millicredits. |
| `pending_balance` | integer (int64) | optional | Credits in a block that starts later. Only present for a metered check. Unit: millicredits. |
| `effective_balance` | integer (int64) | optional | What a new charge can spend, right now. Only present for a metered check. Unit: millicredits. |
| `estimated_cost` | integer (int64) | optional | What `units` would cost, at today’s price. Only present for a metered check. Unit: millicredits. |
| `balance_after` | integer (int64) | optional | How much would be left after this charge. `effective_balance` minus `estimated_cost`, worked out ahead of time. Below zero means the charge costs more than the customer can pay. Under `allow` or `notify`, QuotaStack would log the extra as [overage](/docs/concepts/credits) instead of blocking. Only present for a metered check. Unit: millicredits. |
| `subscription_status` | string | optional | The customer’s [subscription](/docs/concepts/subscriptions) status. `null` with no subscription. Only present for a metered check. |
| `overage_policy` | string | optional | The customer's own value wins when set. The tenant default [overage](/docs/concepts/credits) rule applies when it is not. Only present for a metered check. `block` — Refuse the call once the balance runs out. `allow` — Let the call continue. QuotaStack logs the rest as [overage](/docs/concepts/credits). `notify` — Like `allow`. No [webhook](/docs/concepts/webhooks) fires for the [overage](/docs/concepts/credits) yet. |
| `enabled` | boolean | optional | The flag’s value for this customer. Only present for a boolean check. |
| `config` | object | optional | The free-form value your app should read. Comes from the customer’s [plan variant](/docs/concepts/plan-variant-entitlements), or the metric’s own default. Only present for a static check. |
| `current` | integer (int64) | optional | How many units this customer has already used, for this [gauge](/docs/concepts/entitlements). Only present for a gauge check. Unit: a count of this gauge’s own units, not credits. |
| `cap` | integer (int64) | optional | The most this customer may use, for this [gauge](/docs/concepts/entitlements). Only present for a gauge check. Unit: a count of this gauge’s own units, not credits. |
| `headroom` | integer (int64) | optional | The cap minus how much the customer has used. Below zero once they are already over it. A plan downgrade can cause that, by lowering the cap. Only present for a gauge check. Unit: a count of this gauge’s own units, not credits. |

### Errors

- `400` — A required query parameter is missing: `customer_id` or `billable_metric_key`. A `units` value of zero or less returns this too. So does a `customer_id` that belongs to a different tenant. Use your own `customer_id`, or send your own external id instead. See [bad-request](/docs/api/errors/bad-request).
- `401` — Missing or invalid authentication.
- `404` — No active [metering rule](/docs/concepts/metering) covers this `billable_metric_key`. A `boolean`, `gauge`, or `static` metric never returns this — only a metered check can. See [not-found](/docs/api/errors/not-found).
- `429` — Rate limit exceeded.
- `500` — Unexpected server error.

### See also

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

## Check an entitlement by customer ID and metric

`GET /v1/customers/{id}/entitlements/{metric}`

- **Idempotency-Key:** Not used. This is a read.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Nothing to the [entitlement](/docs/concepts/entitlements) itself. A check never touches a balance or a [subscription](/docs/concepts/subscriptions). An unrecognized customer id is different. QuotaStack makes a new customer for it, the same as a credit grant. The new one starts with no balance and no [subscription](/docs/concepts/subscriptions).
- **Fires:** No webhooks.

`checkCustomerEntitlement` checks the same thing as `checkEntitlement`. The customer and the metric sit in the path here, not the query string. Both return the same result. openapi.yaml lists only `401` and `404` for this call. A bad `units` value still returns `400` in practice. So does a `customer_id` from another tenant.

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required | The customer’s own id, or your own `external_id`. Either one works here. |
| `metric` | string | required | The [billable metric](/docs/concepts/metering)’s own key. Not a QuotaStack id. |

### Query parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `units` | integer | optional | How many units to check for. Only a metered metric uses this — every other type ignores it. If you leave it out: Left out, QuotaStack checks for 1 unit. |

### Response 200

```json
{
  "type": "metered",
  "allowed": true,
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "billable_metric_key": "chat_message",
  "units": 1,
  "balance": 250000,
  "reserved_balance": 0,
  "pending_balance": 0,
  "effective_balance": 250000,
  "estimated_cost": 500,
  "balance_after": 249500,
  "subscription_status": "active",
  "overage_policy": "block"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `type` | string | required | Which [entitlement](/docs/concepts/entitlements) type this metric is. `metered` — Priced by a [metering rule](/docs/concepts/metering), paid out of the customer’s balance. `boolean` — A plain on/off flag. Only `allowed` matters — nothing costs credits. `gauge` — A running count with a cap, like a seat limit. Nothing costs credits either. `static` — A JSON value your app reads directly. QuotaStack never charges for reading it. |
| `allowed` | boolean | required | Whether QuotaStack says yes to this call, right now. |
| `customer_id` | string (uuid) | required | The customer’s own id inside QuotaStack. The same id comes back, no matter which id you sent. |
| `billable_metric_key` | string | required | The [billable metric](/docs/concepts/metering) key you asked about. |
| `units` | integer (int64) | optional | How many units you asked about. Only present for a metered check. Unit: a count of billable metric units, not credits. |
| `balance` | integer (int64) | optional | What the customer has, before holds. Only present for a metered check. Unit: millicredits, where 1 credit is 1000 millicredits. |
| `reserved_balance` | integer (int64) | optional | Credits held by an open [reservation](/docs/concepts/reservations). Only present for a metered check. Unit: millicredits. |
| `pending_balance` | integer (int64) | optional | Credits in a block that starts later. Only present for a metered check. Unit: millicredits. |
| `effective_balance` | integer (int64) | optional | What a new charge can spend, right now. Only present for a metered check. Unit: millicredits. |
| `estimated_cost` | integer (int64) | optional | What `units` would cost, at today’s price. Only present for a metered check. Unit: millicredits. |
| `balance_after` | integer (int64) | optional | How much would be left after this charge. `effective_balance` minus `estimated_cost`, worked out ahead of time. Below zero means the charge costs more than the customer can pay. Under `allow` or `notify`, QuotaStack would log the extra as [overage](/docs/concepts/credits) instead of blocking. Only present for a metered check. Unit: millicredits. |
| `subscription_status` | string | optional | The customer’s [subscription](/docs/concepts/subscriptions) status. `null` with no subscription. Only present for a metered check. |
| `overage_policy` | string | optional | The customer's own value wins when set. The tenant default [overage](/docs/concepts/credits) rule applies when it is not. Only present for a metered check. `block` — Refuse the call once the balance runs out. `allow` — Let the call continue. QuotaStack logs the rest as [overage](/docs/concepts/credits). `notify` — Like `allow`. No [webhook](/docs/concepts/webhooks) fires for the [overage](/docs/concepts/credits) yet. |
| `enabled` | boolean | optional | The flag’s value for this customer. Only present for a boolean check. |
| `config` | object | optional | The free-form value your app should read. Comes from the customer’s [plan variant](/docs/concepts/plan-variant-entitlements), or the metric’s own default. Only present for a static check. |
| `current` | integer (int64) | optional | How many units this customer has already used, for this [gauge](/docs/concepts/entitlements). Only present for a gauge check. Unit: a count of this gauge’s own units, not credits. |
| `cap` | integer (int64) | optional | The most this customer may use, for this [gauge](/docs/concepts/entitlements). Only present for a gauge check. Unit: a count of this gauge’s own units, not credits. |
| `headroom` | integer (int64) | optional | The cap minus how much the customer has used. Below zero once they are already over it. A plan downgrade can cause that, by lowering the cap. Only present for a gauge check. Unit: a count of this gauge’s own units, not credits. |

### Errors

- `401` — Missing or invalid authentication.
- `404` — No active [metering rule](/docs/concepts/metering) covers this `metric`. A `boolean`, `gauge`, or `static` metric never returns this. See [not-found](/docs/api/errors/not-found).

### See also

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

## Get every entitlement for a customer

`GET /v1/customers/{id}/entitlements`

- **Idempotency-Key:** Not used. This is a read.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Nothing to the [entitlement](/docs/concepts/entitlements) itself. A check never touches a balance or a [subscription](/docs/concepts/subscriptions). An unrecognized customer id is different. QuotaStack makes a new customer for it, the same as a credit grant. The new one starts with no balance and no [subscription](/docs/concepts/subscriptions).
- **Fires:** No webhooks.

This call returns one snapshot for one customer: the balance, the [subscription](/docs/concepts/subscriptions), and every [entitlement](/docs/concepts/entitlements) the tenant has defined. QuotaStack lists every [billable metric](/docs/concepts/metering) the tenant has. A customer’s plan does not filter this list. `allowed` says whether this customer can use each one right now. QuotaStack caches this snapshot for up to 30 seconds. Send `Cache-Control: no-cache` to read a fresh one instead. The response also carries a `pending_balance` field, in [millicredits](/docs/concepts/credits). openapi.yaml does not yet declare it.

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required | The customer’s own id, or your own `external_id`. Either one works here. |

### Response 200

```json
{
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "environment": "live",
  "balance": 250000,
  "reserved_balance": 0,
  "pending_balance": 0,
  "effective_balance": 250000,
  "subscription_status": "active",
  "plan_name": "Pro",
  "entitlements": {
    "chat_message": {
      "type": "metered",
      "billable_metric_key": "chat_message",
      "allowed": true,
      "estimated_cost_per_unit": 500,
      "affordable_units": 500,
      "cost_type": "per_unit"
    },
    "sso": {
      "type": "boolean",
      "billable_metric_key": "sso",
      "allowed": true,
      "enabled": true
    },
    "seats": {
      "type": "gauge",
      "billable_metric_key": "seats",
      "allowed": true,
      "current": 3,
      "cap": 5,
      "headroom": 2
    }
  },
  "cached_at": "2026-07-27T10:00:00Z"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `customer_id` | string (uuid) | required | This customer’s id inside QuotaStack. The same id comes back, no matter which id you sent. |
| `environment` | string | required | Sandbox or live. Matches the API key you called with. `live` `sandbox` |
| `balance` | integer (int64) | required | What the customer has, before holds. Unit: millicredits, where 1 credit is 1000 millicredits. |
| `reserved_balance` | integer (int64) | required | Credits held by an open [reservation](/docs/concepts/reservations). Unit: millicredits. |
| `effective_balance` | integer (int64) | required | What a new charge can spend, right now. Unit: millicredits. |
| `subscription_status` | string | optional | The customer’s [subscription](/docs/concepts/subscriptions) status. `null` with no subscription. |
| `plan_name` | string | optional | The customer’s plan name. `null` with no [subscription](/docs/concepts/subscriptions). |
| `entitlements` | object | required | One entry per [billable metric](/docs/concepts/metering) your tenant has, keyed by its `billable_metric_key`. Every metric your tenant defines shows up here, whether or not the customer’s plan grants it. `allowed` says whether they can use it right now. Each entry carries its own `type`: `metered`, `boolean`, `gauge`, or `static`. A metered entry adds `estimated_cost_per_unit`, `affordable_units`, and `cost_type`. A boolean entry adds `enabled`. A gauge entry adds `current`, `cap`, and `headroom`. A static entry adds `config`. |
| `cached_at` | string (date-time) | required | When QuotaStack computed this snapshot. The snapshot can be up to 30 seconds old. |

### Errors

- `401` — Missing or invalid authentication.
- `404` — Declared here, but never reached. An unrecognized `customer_id` makes a new customer, instead of a 404. A `customer_id` from another tenant gets `400` instead. See [not-found](/docs/api/errors/not-found).

### See also

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

## Check an entitlement by external ID and metric

`GET /v1/customer-by-external-id/{external_id}/entitlements/{metric}`

- **Idempotency-Key:** Not used. This is a read.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Nothing to the [entitlement](/docs/concepts/entitlements) itself. A check never touches a balance or a [subscription](/docs/concepts/subscriptions).
- **Fires:** No webhooks.

This one works the same as `checkCustomerEntitlement` does. You use your own id here, not the QuotaStack one. A bad id here never makes a new customer — you get a `404` back instead. A bad `units` number still gives you a `400`, in real use.

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `external_id` | string | required | Your own ID for this customer, set when you created it. QuotaStack looks up only this value, never the QuotaStack UUID. If you leave it out: QuotaStack rejects the call. This path segment is required. |
| `metric` | string | required | The [billable metric](/docs/concepts/metering)’s own key. Not a QuotaStack id. |

### Query parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `units` | integer | optional | How many units to check for. Only a metered metric uses this — every other type ignores it. If you leave it out: Left out, QuotaStack checks for 1 unit. |

### Response 200

```json
{
  "type": "metered",
  "allowed": true,
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "billable_metric_key": "chat_message",
  "units": 1,
  "balance": 250000,
  "reserved_balance": 0,
  "pending_balance": 0,
  "effective_balance": 250000,
  "estimated_cost": 500,
  "balance_after": 249500,
  "subscription_status": "active",
  "overage_policy": "block"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `type` | string | required | Which [entitlement](/docs/concepts/entitlements) type this metric is. `metered` — Priced by a [metering rule](/docs/concepts/metering), paid out of the customer’s balance. `boolean` — A plain on/off flag. Only `allowed` matters — nothing costs credits. `gauge` — A running count with a cap, like a seat limit. Nothing costs credits either. `static` — A JSON value your app reads directly. QuotaStack never charges for reading it. |
| `allowed` | boolean | required | Whether QuotaStack says yes to this call, right now. |
| `customer_id` | string (uuid) | required | The customer’s own id inside QuotaStack. The same id comes back, no matter which id you sent. |
| `billable_metric_key` | string | required | The [billable metric](/docs/concepts/metering) key you asked about. |
| `units` | integer (int64) | optional | How many units you asked about. Only present for a metered check. Unit: a count of billable metric units, not credits. |
| `balance` | integer (int64) | optional | What the customer has, before holds. Only present for a metered check. Unit: millicredits, where 1 credit is 1000 millicredits. |
| `reserved_balance` | integer (int64) | optional | Credits held by an open [reservation](/docs/concepts/reservations). Only present for a metered check. Unit: millicredits. |
| `pending_balance` | integer (int64) | optional | Credits in a block that starts later. Only present for a metered check. Unit: millicredits. |
| `effective_balance` | integer (int64) | optional | What a new charge can spend, right now. Only present for a metered check. Unit: millicredits. |
| `estimated_cost` | integer (int64) | optional | What `units` would cost, at today’s price. Only present for a metered check. Unit: millicredits. |
| `balance_after` | integer (int64) | optional | How much would be left after this charge. `effective_balance` minus `estimated_cost`, worked out ahead of time. Below zero means the charge costs more than the customer can pay. Under `allow` or `notify`, QuotaStack would log the extra as [overage](/docs/concepts/credits) instead of blocking. Only present for a metered check. Unit: millicredits. |
| `subscription_status` | string | optional | The customer’s [subscription](/docs/concepts/subscriptions) status. `null` with no subscription. Only present for a metered check. |
| `overage_policy` | string | optional | The customer's own value wins when set. The tenant default [overage](/docs/concepts/credits) rule applies when it is not. Only present for a metered check. `block` — Refuse the call once the balance runs out. `allow` — Let the call continue. QuotaStack logs the rest as [overage](/docs/concepts/credits). `notify` — Like `allow`. No [webhook](/docs/concepts/webhooks) fires for the [overage](/docs/concepts/credits) yet. |
| `enabled` | boolean | optional | The flag’s value for this customer. Only present for a boolean check. |
| `config` | object | optional | The free-form value your app should read. Comes from the customer’s [plan variant](/docs/concepts/plan-variant-entitlements), or the metric’s own default. Only present for a static check. |
| `current` | integer (int64) | optional | How many units this customer has already used, for this [gauge](/docs/concepts/entitlements). Only present for a gauge check. Unit: a count of this gauge’s own units, not credits. |
| `cap` | integer (int64) | optional | The most this customer may use, for this [gauge](/docs/concepts/entitlements). Only present for a gauge check. Unit: a count of this gauge’s own units, not credits. |
| `headroom` | integer (int64) | optional | The cap minus how much the customer has used. Below zero once they are already over it. A plan downgrade can cause that, by lowering the cap. Only present for a gauge check. Unit: a count of this gauge’s own units, not credits. |

### Errors

- `401` — Missing or invalid authentication.
- `404` — No customer here has this `external_id`, or no active [metering rule](/docs/concepts/metering) covers this `metric`. See [not-found](/docs/api/errors/not-found).

### See also

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

## Get every entitlement by external ID

`GET /v1/customer-by-external-id/{external_id}/entitlements`

- **Idempotency-Key:** Not used. This is a read.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Nothing to the [entitlement](/docs/concepts/entitlements) itself. A check never touches a balance or a [subscription](/docs/concepts/subscriptions).
- **Fires:** No webhooks.

`getCustomerEntitlementsByExternalId` is the `external_id` twin of `getCustomerEntitlements`. An unrecognized `external_id` never creates a customer here. QuotaStack returns `404` instead. Here, unlike the `id` form, `404` is real and reachable.

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `external_id` | string | required | Your own ID for this customer, set when you created it. QuotaStack looks up only this value, never the QuotaStack UUID. If you leave it out: QuotaStack rejects the call. This path segment is required. |

### Response 200

```json
{
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "environment": "live",
  "balance": 250000,
  "reserved_balance": 0,
  "pending_balance": 0,
  "effective_balance": 250000,
  "subscription_status": "active",
  "plan_name": "Pro",
  "entitlements": {
    "chat_message": {
      "type": "metered",
      "billable_metric_key": "chat_message",
      "allowed": true,
      "estimated_cost_per_unit": 500,
      "affordable_units": 500,
      "cost_type": "per_unit"
    },
    "sso": {
      "type": "boolean",
      "billable_metric_key": "sso",
      "allowed": true,
      "enabled": true
    },
    "seats": {
      "type": "gauge",
      "billable_metric_key": "seats",
      "allowed": true,
      "current": 3,
      "cap": 5,
      "headroom": 2
    }
  },
  "cached_at": "2026-07-27T10:00:00Z"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `customer_id` | string (uuid) | required | This customer’s id inside QuotaStack. The same id comes back, no matter which id you sent. |
| `environment` | string | required | Sandbox or live. Matches the API key you called with. `live` `sandbox` |
| `balance` | integer (int64) | required | What the customer has, before holds. Unit: millicredits, where 1 credit is 1000 millicredits. |
| `reserved_balance` | integer (int64) | required | Credits held by an open [reservation](/docs/concepts/reservations). Unit: millicredits. |
| `effective_balance` | integer (int64) | required | What a new charge can spend, right now. Unit: millicredits. |
| `subscription_status` | string | optional | The customer’s [subscription](/docs/concepts/subscriptions) status. `null` with no subscription. |
| `plan_name` | string | optional | The customer’s plan name. `null` with no [subscription](/docs/concepts/subscriptions). |
| `entitlements` | object | required | One entry per [billable metric](/docs/concepts/metering) your tenant has, keyed by its `billable_metric_key`. Every metric your tenant defines shows up here, whether or not the customer’s plan grants it. `allowed` says whether they can use it right now. Each entry carries its own `type`: `metered`, `boolean`, `gauge`, or `static`. A metered entry adds `estimated_cost_per_unit`, `affordable_units`, and `cost_type`. A boolean entry adds `enabled`. A gauge entry adds `current`, `cap`, and `headroom`. A static entry adds `config`. |
| `cached_at` | string (date-time) | required | When QuotaStack computed this snapshot. The snapshot can be up to 30 seconds old. |

### Errors

- `401` — Missing or invalid authentication.
- `404` — No customer here has this `external_id`. See [not-found](/docs/api/errors/not-found).

### See also

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

## Increment a gauge

`POST /v1/customers/{id}/entitlements/{billable_metric_key}/increment`

- **Idempotency-Key:** Required. Returns 422 without it.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Adds `amount` to this customer’s gauge count for this metric. First use makes the counter at zero, then applies the increment. An unrecognized customer id is different. QuotaStack makes a new customer for it, the same as a credit grant. The new one starts with no balance and no [subscription](/docs/concepts/subscriptions).
- **Fires:** No webhooks.

A [gauge](/docs/concepts/entitlements) tracks a running count with a cap, like seats or storage. Increment adds `amount` to the count. The cap comes from the customer’s [plan variant](/docs/concepts/plan-variant-entitlements), or the metric’s own default. With `policy: block`, the default, an increment past the cap is refused. With `policy: allow`, QuotaStack lets it through instead — `headroom` in the response goes negative.

### Header parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `Idempotency-Key` | string | required | Required idempotency key for this mutating admin/public operation. Reusing the same key with a different body returns `409 Conflict`; successful 2xx and client 4xx responses are cached for 24 hours. |

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required | The customer’s own id, or your own `external_id`. Either one works here. |
| `billable_metric_key` | string | required | The [billable metric](/docs/concepts/metering)’s own key. Not a QuotaStack id. |

### Body parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `amount` | integer (int64) | required | How much to change the gauge by — added on increment, subtracted on decrement. Unit: a count of this gauge’s own units, not credits. If you leave it out: QuotaStack rejects the call. Must be a whole number above zero. Range: 1–∞. |
| `policy` | string | optional | What to do when the change would cross the cap. Only `incrementGauge` and its `external_id` twin act on this. `decrementGauge` takes the field too, but ignores it — a decrement can never cross the cap upward. If you leave it out: Left out, QuotaStack uses `block`. `block` — Refuse the increment. The count stays where it was. `allow` — Let the increment through, even past the cap. `headroom` in the response goes negative. |
| `metadata` | object | optional | Your own key-value tags. QuotaStack takes this field but throws it away — nothing is stored. If you leave it out: Nothing changes either way. The field has no effect. |

### Request

```json
{
  "amount": 1
}
```

### Response 200

```json
{
  "billable_metric_key": "seats",
  "current": 4,
  "cap": 5,
  "allowed": true,
  "headroom": 1
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `billable_metric_key` | string | required | The [gauge](/docs/concepts/entitlements) metric this change touched. |
| `current` | integer (int64) | required | The gauge’s count after this change. Unit: a count of this gauge’s own units, not credits. |
| `cap` | integer (int64) | required | The cap in force right now. Comes from the customer’s [plan variant](/docs/concepts/plan-variant-entitlements), or the metric’s own default value. Unit: a count of this gauge’s own units, not credits. |
| `allowed` | boolean | required | Whether `current` still sits below `cap`, after this change. |
| `headroom` | integer (int64) | required | `cap` minus `current`. Negative once `policy: allow` lets an increment cross the cap. Unit: a count of this gauge’s own units, not credits. |

### Errors

- `400` — `amount` is zero or negative. `policy` can also be wrong — anything other than `block` or `allow`. Or the metric is not a `gauge` type. So does a `customer_id` that belongs to a different tenant. Use your own `customer_id`, or send your own external id instead. See [bad-request](/docs/api/errors/bad-request).
- `401` — Missing or invalid authentication.
- `404` — No [billable metric](/docs/concepts/metering) here has this key at all. See [not-found](/docs/api/errors/not-found).
- `409` — Two different bodies come back here. Under `policy: block`, crossing the cap sends back `current`, `cap`, and `shortfall`. A version clash sends back a plain conflict instead — retry it. See [conflict](/docs/api/errors/conflict).

### See also

- [Entitlements](/docs/concepts/entitlements)
- [Setting up typed entitlements](/docs/cookbook/entitlement-types-setup)

## Decrement a gauge

`POST /v1/customers/{id}/entitlements/{billable_metric_key}/decrement`

- **Idempotency-Key:** Required. Returns 422 without it.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Subtracts `amount` from this customer’s gauge count for this metric. An unrecognized customer id is different. QuotaStack makes a new customer for it, the same as a credit grant. The new one starts with no balance and no [subscription](/docs/concepts/subscriptions).
- **Fires:** No webhooks.

Decrement subtracts `amount` from the customer’s gauge count. The count never goes below zero. QuotaStack refuses a decrement that would take it there. The request takes a `policy` field, the same one `incrementGauge` uses, but decrement ignores it.

### Header parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `Idempotency-Key` | string | required | Required idempotency key for this mutating admin/public operation. Reusing the same key with a different body returns `409 Conflict`; successful 2xx and client 4xx responses are cached for 24 hours. |

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required | The customer’s own id, or your own `external_id`. Either one works here. |
| `billable_metric_key` | string | required | The [billable metric](/docs/concepts/metering)’s own key. Not a QuotaStack id. |

### Body parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `amount` | integer (int64) | required | How much to change the gauge by — added on increment, subtracted on decrement. Unit: a count of this gauge’s own units, not credits. If you leave it out: QuotaStack rejects the call. Must be a whole number above zero. Range: 1–∞. |
| `policy` | string | optional | What to do when the change would cross the cap. Only `incrementGauge` and its `external_id` twin act on this. `decrementGauge` takes the field too, but ignores it — a decrement can never cross the cap upward. If you leave it out: Left out, QuotaStack uses `block`. `block` — Refuse the increment. The count stays where it was. `allow` — Let the increment through, even past the cap. `headroom` in the response goes negative. |
| `metadata` | object | optional | Your own key-value tags. QuotaStack takes this field but throws it away — nothing is stored. If you leave it out: Nothing changes either way. The field has no effect. |

### Request

```json
{
  "amount": 1
}
```

### Response 200

```json
{
  "billable_metric_key": "seats",
  "current": 2,
  "cap": 5,
  "allowed": true,
  "headroom": 3
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `billable_metric_key` | string | required | The [gauge](/docs/concepts/entitlements) metric this change touched. |
| `current` | integer (int64) | required | The gauge’s count after this change. Unit: a count of this gauge’s own units, not credits. |
| `cap` | integer (int64) | required | The cap in force right now. Comes from the customer’s [plan variant](/docs/concepts/plan-variant-entitlements), or the metric’s own default value. Unit: a count of this gauge’s own units, not credits. |
| `allowed` | boolean | required | Whether `current` still sits below `cap`, after this change. |
| `headroom` | integer (int64) | required | `cap` minus `current`. Negative once `policy: allow` lets an increment cross the cap. Unit: a count of this gauge’s own units, not credits. |

### Errors

- `400` — `amount` is zero or negative. The metric can also be the wrong type — not a `gauge`. A customer id from a different tenant returns this too. See [bad-request](/docs/api/errors/bad-request).
- `401` — Missing or invalid authentication.
- `404` — No [billable metric](/docs/concepts/metering) here has this key at all. See [not-found](/docs/api/errors/not-found).
- `409` — The decrement would take the count below zero. Or another mutation on the same gauge ran at the same time. Retry either one. See [conflict](/docs/api/errors/conflict).

### See also

- [Entitlements](/docs/concepts/entitlements)
- [Setting up typed entitlements](/docs/cookbook/entitlement-types-setup)

## Check and charge in one call

`POST /v1/entitlements/consume`

- **Idempotency-Key:** Required. Returns 422 without it.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Writes one debit to the [ledger](/docs/concepts/credits), unless a `reason` value says nothing was charged. An unrecognized customer id is different. QuotaStack makes a new customer for it, the same as a credit grant. The new one starts with no balance and no [subscription](/docs/concepts/subscriptions).
- **Fires:** [`credit.consumed`](/docs/api/events/credit.consumed), [`credit.low_balance`](/docs/api/events/credit.low_balance), [`credit.exhausted`](/docs/api/events/credit.exhausted)

A consume checks whether the customer can afford `units` of a metered metric. QuotaStack charges them too, in the same step. Only a [metered](/docs/concepts/metering) metric works here. Sending a `boolean`, `gauge`, or `static` metric’s key returns `400`. An exact repeat of the same `Idempotency-Key` replays from cache for 24 hours, like every write. The `Idempotency-Key` cache can expire before you retry. `reason: duplicate_replay` catches that case too. QuotaStack replays the ORIGINAL numbers, not new ones.

### Header parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `Idempotency-Key` | string | required | Required idempotency key for this mutating admin/public operation. Reusing the same key with a different body returns `409 Conflict`; successful 2xx and client 4xx responses are cached for 24 hours. |

### Body parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `customer_id` | string | required | The customer’s own id, or your own `external_id`. Either works here. If you leave it out: QuotaStack rejects the call. This field is required. |
| `billable_metric_key` | string | required | Which [metered](/docs/concepts/metering) metric to charge for. This metric needs an active [metering rule](/docs/concepts/metering). If you leave it out: QuotaStack rejects the call. This field is required. |
| `units` | integer (int64) | required | How many units this call used. Unit: a count of billable metric units, not credits. If you leave it out: QuotaStack rejects the call. Must be at least 1. Range: 1–∞. |
| `metadata` | object | optional | Your own key-value tags. QuotaStack writes them onto the [ledger](/docs/concepts/credits) entry this call makes. If you leave it out: QuotaStack writes no tags onto the [ledger](/docs/concepts/credits) entry. |

### Request

```json
{
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "billable_metric_key": "chat_message",
  "units": 10
}
```

### Response 200

```json
{
  "type": "metered",
  "allowed": true,
  "debited": true,
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "billable_metric_key": "chat_message",
  "units": 10,
  "estimated_cost": 5000,
  "balance": 245000,
  "reserved_balance": 0,
  "pending_balance": 0,
  "effective_balance": 245000,
  "subscription_status": "active",
  "overage_policy": "block"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `type` | string | required | Always `metered`. Only a metered metric ever reaches this endpoint. `metered` |
| `allowed` | boolean | required | Whether QuotaStack says yes to this call, right now. |
| `debited` | boolean | required | Whether QuotaStack took any credits at all. `true` even for a partial [overage](/docs/concepts/credits) debit. |
| `reason` | string | optional | Why the call ended this way. QuotaStack only sends this field when something notable happened. A normal call has no reason. `insufficient_credits` — The customer did not have enough credits. Nothing was charged. `overage_recorded` — The customer ran out of credits partway through. QuotaStack charged what they had. QuotaStack wrote down the rest as [overage](/docs/concepts/credits). The overage field holds that amount. `overdue_blocked` — The customer's [subscription](/docs/concepts/subscriptions) is overdue. Nothing was charged. `zero_cost` — This metric costs nothing. Nothing was charged. `duplicate_replay` — You already sent this request. QuotaStack already charged for it then. QuotaStack does not charge again now. `units` and `estimated_cost` show the first request's own numbers, not this one's. |
| `original_reason` | string | optional | The first call’s own `reason`, replayed here. Only present when `reason` is `duplicate_replay` and that first call was `overage_recorded`. `overage_recorded` |
| `customer_id` | string (uuid) | required | The customer’s own id inside QuotaStack. The same id comes back, no matter which id you sent. |
| `billable_metric_key` | string | required | The metric key this call charged against. |
| `units` | integer (int64) | required | The units this call charged for. On `reason: duplicate_replay`, this is the FIRST call’s units, not this one’s. Unit: a count of billable metric units, not credits. |
| `estimated_cost` | integer (int64) | required | What `units` cost, at the price in effect. On `reason: duplicate_replay`, this is the FIRST call’s cost, not this one’s. Unit: millicredits, where 1 credit is 1000 millicredits. |
| `balance` | integer (int64) | required | What the customer has, before holds, read fresh after this call. Unit: millicredits. |
| `reserved_balance` | integer (int64) | required | Credits held by an open [reservation](/docs/concepts/reservations). A consume never changes this. Unit: millicredits. |
| `pending_balance` | integer (int64) | required | Credits in a block that starts later. Unit: millicredits. |
| `effective_balance` | integer (int64) | required | What a new charge can spend, right after this call. Unit: millicredits. |
| `shortfall` | integer (int64) | optional | `estimated_cost` minus `effective_balance`. Only present when `reason` is `insufficient_credits`. Unit: millicredits. |
| `overage` | integer (int64) | optional | Credit spent past the balance, logged for arrears billing later. Only present when `reason` is `overage_recorded`, or a `duplicate_replay` of one. Unit: millicredits. |
| `subscription_status` | string | required | The customer’s [subscription](/docs/concepts/subscriptions) status. `null` with no subscription. |
| `overage_policy` | string | required | The customer's own value wins when set. The tenant default [overage](/docs/concepts/credits) rule applies when it is not. `block` — Refuse the call once the balance runs out. `allow` — Let the call continue. QuotaStack logs the rest as [overage](/docs/concepts/credits). `notify` — Like `allow`. No [webhook](/docs/concepts/webhooks) fires for the [overage](/docs/concepts/credits) yet. |

### The normal case

The customer had enough credit. QuotaStack went ahead and charged them.

```json
{
  "type": "metered",
  "allowed": true,
  "debited": true,
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "billable_metric_key": "chat_message",
  "units": 10,
  "estimated_cost": 5000,
  "balance": 245000,
  "reserved_balance": 0,
  "pending_balance": 0,
  "effective_balance": 245000,
  "subscription_status": "active",
  "overage_policy": "block"
}
```

### Not enough credits

QuotaStack charged nothing at all. Your app should stop right here.

```json
{
  "type": "metered",
  "allowed": false,
  "debited": false,
  "reason": "insufficient_credits",
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "billable_metric_key": "chat_message",
  "units": 10,
  "estimated_cost": 5000,
  "balance": 2000,
  "reserved_balance": 0,
  "pending_balance": 0,
  "effective_balance": 2000,
  "shortfall": 3000,
  "subscription_status": "active",
  "overage_policy": "block"
}
```

### Ran out partway through

QuotaStack charged what the customer had, and logged the rest as [overage](/docs/concepts/credits). You bill for that part later.

```json
{
  "type": "metered",
  "allowed": true,
  "debited": true,
  "reason": "overage_recorded",
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "billable_metric_key": "chat_message",
  "units": 10,
  "estimated_cost": 5000,
  "balance": 0,
  "reserved_balance": 0,
  "pending_balance": 0,
  "effective_balance": 0,
  "overage": 2000,
  "subscription_status": "active",
  "overage_policy": "allow"
}
```

### You sent the same key twice

QuotaStack charged nothing the second time. The numbers you get back are from the first call.

```json
{
  "type": "metered",
  "allowed": true,
  "debited": true,
  "reason": "duplicate_replay",
  "original_reason": "overage_recorded",
  "customer_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "billable_metric_key": "chat_message",
  "units": 10,
  "estimated_cost": 5000,
  "balance": 0,
  "reserved_balance": 0,
  "pending_balance": 0,
  "effective_balance": 0,
  "overage": 2000,
  "subscription_status": "active",
  "overage_policy": "allow"
}
```

### Errors

- `400` — `customer_id`, `billable_metric_key`, or a positive `units` value is missing. A `boolean`, `gauge`, or `static` metric also returns this. So does a customer id from a different tenant. See [bad-request](/docs/api/errors/bad-request).
- `401` — Missing or invalid authentication.
- `404` — No active [metering rule](/docs/concepts/metering) covers this `billable_metric_key`. See [not-found](/docs/api/errors/not-found).
- `409` — You reused this `Idempotency-Key` with a different `customer_id`, `billable_metric_key`, or `units`. The hash QuotaStack compares ignores `metadata` — changing only that still replays. See [idempotency-key-reuse](/docs/api/errors/idempotency-key-reuse).
- `422` — The `Idempotency-Key` header is missing here. QuotaStack’s [idempotency](/docs/concepts/idempotency) middleware catches this before the handler runs. See [validation-error](/docs/api/errors/validation-error).

### See also

- [Entitlements](/docs/concepts/entitlements)
- [Metering](/docs/concepts/metering)
- [Idempotency](/docs/concepts/idempotency)
- [Usage](/docs/api/usage)

## Increment a gauge by external ID

`POST /v1/customer-by-external-id/{external_id}/entitlements/{billable_metric_key}/increment`

- **Idempotency-Key:** Required. Returns 422 without it.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Adds `amount` to this customer’s gauge count for this metric. First use makes the counter at zero, then applies the increment.
- **Fires:** No webhooks.

`incrementGaugeByExternalId` works the same as `incrementGauge`. An unrecognized `external_id` never makes a customer here. `404` comes back instead.

### Header parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `Idempotency-Key` | string | required | Required idempotency key for this mutating admin/public operation. Reusing the same key with a different body returns `409 Conflict`; successful 2xx and client 4xx responses are cached for 24 hours. |

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `external_id` | string | required | Your own ID for this customer, set when you created it. QuotaStack looks up only this value, never the QuotaStack UUID. If you leave it out: QuotaStack rejects the call. This path segment is required. |
| `billable_metric_key` | string | required | The [billable metric](/docs/concepts/metering)’s own key. Not a QuotaStack id. |

### Body parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `amount` | integer (int64) | required | How much to change the gauge by — added on increment, subtracted on decrement. Unit: a count of this gauge’s own units, not credits. If you leave it out: QuotaStack rejects the call. Must be a whole number above zero. Range: 1–∞. |
| `policy` | string | optional | What to do when the change would cross the cap. Only `incrementGauge` and its `external_id` twin act on this. `decrementGauge` takes the field too, but ignores it — a decrement can never cross the cap upward. If you leave it out: Left out, QuotaStack uses `block`. `block` — Refuse the increment. The count stays where it was. `allow` — Let the increment through, even past the cap. `headroom` in the response goes negative. |
| `metadata` | object | optional | Your own key-value tags. QuotaStack takes this field but throws it away — nothing is stored. If you leave it out: Nothing changes either way. The field has no effect. |

### Request

```json
{
  "amount": 1
}
```

### Response 200

```json
{
  "billable_metric_key": "seats",
  "current": 4,
  "cap": 5,
  "allowed": true,
  "headroom": 1
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `billable_metric_key` | string | required | The [gauge](/docs/concepts/entitlements) metric this change touched. |
| `current` | integer (int64) | required | The gauge’s count after this change. Unit: a count of this gauge’s own units, not credits. |
| `cap` | integer (int64) | required | The cap in force right now. Comes from the customer’s [plan variant](/docs/concepts/plan-variant-entitlements), or the metric’s own default value. Unit: a count of this gauge’s own units, not credits. |
| `allowed` | boolean | required | Whether `current` still sits below `cap`, after this change. |
| `headroom` | integer (int64) | required | `cap` minus `current`. Negative once `policy: allow` lets an increment cross the cap. Unit: a count of this gauge’s own units, not credits. |

### Errors

- `400` — `amount` is zero or negative. `policy` can also be wrong — anything other than `block` or `allow`. Or the metric is not a `gauge` type. See [bad-request](/docs/api/errors/bad-request).
- `401` — Missing or invalid authentication.
- `404` — No customer here has this `external_id`, or no [billable metric](/docs/concepts/metering) has this key at all. See [not-found](/docs/api/errors/not-found).
- `409` — Two different bodies come back here. Under `policy: block`, crossing the cap sends back `current`, `cap`, and `shortfall`. A version clash sends back a plain conflict instead — retry it. See [conflict](/docs/api/errors/conflict).

### See also

- [Entitlements](/docs/concepts/entitlements)
- [Setting up typed entitlements](/docs/cookbook/entitlement-types-setup)

## Decrement a gauge by external ID

`POST /v1/customer-by-external-id/{external_id}/entitlements/{billable_metric_key}/decrement`

- **Idempotency-Key:** Required. Returns 422 without it.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Subtracts `amount` from this customer’s gauge count for this metric.
- **Fires:** No webhooks.

`decrementGaugeByExternalId` works the same as `decrementGauge`. An unrecognized `external_id` never makes a customer here. `404` comes back instead.

### Header parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `Idempotency-Key` | string | required | Required idempotency key for this mutating admin/public operation. Reusing the same key with a different body returns `409 Conflict`; successful 2xx and client 4xx responses are cached for 24 hours. |

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `external_id` | string | required | Your own ID for this customer, set when you created it. QuotaStack looks up only this value, never the QuotaStack UUID. If you leave it out: QuotaStack rejects the call. This path segment is required. |
| `billable_metric_key` | string | required | The [billable metric](/docs/concepts/metering)’s own key. Not a QuotaStack id. |

### Body parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `amount` | integer (int64) | required | How much to change the gauge by — added on increment, subtracted on decrement. Unit: a count of this gauge’s own units, not credits. If you leave it out: QuotaStack rejects the call. Must be a whole number above zero. Range: 1–∞. |
| `policy` | string | optional | What to do when the change would cross the cap. Only `incrementGauge` and its `external_id` twin act on this. `decrementGauge` takes the field too, but ignores it — a decrement can never cross the cap upward. If you leave it out: Left out, QuotaStack uses `block`. `block` — Refuse the increment. The count stays where it was. `allow` — Let the increment through, even past the cap. `headroom` in the response goes negative. |
| `metadata` | object | optional | Your own key-value tags. QuotaStack takes this field but throws it away — nothing is stored. If you leave it out: Nothing changes either way. The field has no effect. |

### Request

```json
{
  "amount": 1
}
```

### Response 200

```json
{
  "billable_metric_key": "seats",
  "current": 2,
  "cap": 5,
  "allowed": true,
  "headroom": 3
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `billable_metric_key` | string | required | The [gauge](/docs/concepts/entitlements) metric this change touched. |
| `current` | integer (int64) | required | The gauge’s count after this change. Unit: a count of this gauge’s own units, not credits. |
| `cap` | integer (int64) | required | The cap in force right now. Comes from the customer’s [plan variant](/docs/concepts/plan-variant-entitlements), or the metric’s own default value. Unit: a count of this gauge’s own units, not credits. |
| `allowed` | boolean | required | Whether `current` still sits below `cap`, after this change. |
| `headroom` | integer (int64) | required | `cap` minus `current`. Negative once `policy: allow` lets an increment cross the cap. Unit: a count of this gauge’s own units, not credits. |

### Errors

- `400` — `amount` is zero or negative, or the metric is not a `gauge` type. See [bad-request](/docs/api/errors/bad-request).
- `401` — Missing or invalid authentication.
- `404` — No customer here has this `external_id`, or no [billable metric](/docs/concepts/metering) has this key at all. See [not-found](/docs/api/errors/not-found).
- `409` — The decrement would take the count below zero. Or another mutation on the same gauge ran at the same time. Retry either one. See [conflict](/docs/api/errors/conflict).

### See also

- [Entitlements](/docs/concepts/entitlements)
- [Setting up typed entitlements](/docs/cookbook/entitlement-types-setup)
