---
title: "Overrides"
description: "Change one entitlement for one subscription, without building a new plan for it."
---

# Overrides

Change one customer without a new plan.

An override changes what a single customer gets. The
[plan](/docs/concepts/plans-and-variants) stays as it is, and everyone else on it keeps
the old value.

Sales promised one customer 25 seats when the plan gives 10. That is an
override. The change lives on their
[subscription](/docs/concepts/subscriptions), and beats the plan value on
every check.

Boolean, gauge, and static metrics can be changed this way. A metered metric
cannot. Change what a metered metric costs with a
[metering rule](/docs/concepts/metering) instead.

One subscription holds one override per metric. Asking for a second one on the
same metric returns a conflict.

An override can end on its own. Set `expires_at` and QuotaStack goes back to
the plan value after that date, with nothing for you to remember.

Writing the same override for the third time is a sign. Build a
[plan variant](/docs/concepts/plan-variant-entitlements) instead, and move
those customers onto it.

## Which call do I want?

- One customer needs a different entitlement, and only that customer → `POST /v1/subscriptions/{id}/overrides`
- Many customers need it → `createPlanVariant`
  An override changes one [subscription](/docs/concepts/subscriptions). A new [plan variant](/docs/concepts/plan-variant-entitlements) changes everyone on it. Reach for a variant when you find yourself writing the same override twice.
- You want to see what a customer already has, override or not → `checkEntitlement`
  This object lists only the changes. The [entitlement](/docs/concepts/entitlements) check tells you what the customer really gets.
- The special treatment should end on a date → `POST /v1/subscriptions/{id}/overrides`
  Set `expires_at`. QuotaStack goes back to the plan value on its own after that.

4 of 4 operations documented in full.

## List overrides

`GET /v1/subscriptions/{id}/overrides`

- **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 call returns every live override on one [subscription](/docs/concepts/subscriptions). The reply is shaped `{"overrides": [...]}`. Other lists on this API use `data` and a `pagination` block. This one does neither. The whole set comes back at once, and there are no pages to walk. The list holds only the changes you made. Use `checkEntitlement` to see the value a customer really gets.

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required |  |

### Response 200

```json
{
  "overrides": [
    {
      "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a73",
      "subscription_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a21",
      "billable_metric_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a64",
      "billable_metric_key": "seats",
      "value": {
        "cap": 25
      },
      "expires_at": null,
      "reason": "Enterprise trial, agreed with sales",
      "created_at": "2026-07-28T09:00:00Z",
      "updated_at": "2026-07-28T09:00:00Z"
    }
  ]
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `overrides` | array | optional |  |
| `overrides.subscription_id` | string (uuid) | required | Which [subscription](/docs/concepts/subscriptions) this change belongs to. One override covers one subscription, and no other. |
| `overrides.billable_metric_id` | string (uuid) | required | QuotaStack's own ID for that metric. Match on `billable_metric_key` instead; the key is the one you chose. |
| `overrides.billable_metric_key` | string | optional | Which [billable metric](/docs/concepts/metering) this change is about. |
| `overrides.value` | object | required | The new value, shaped to match the metric type. A boolean metric takes `{"enabled": true}`. A gauge metric takes `{"cap": 250}`. A static metric takes `{"config": {...}}`. QuotaStack checks the shape against the metric, and returns `400` when the two disagree. |
| `overrides.expires_at` | string (date-time) | optional | When this change stops applying. The [plan](/docs/concepts/plans-and-variants) value takes over again after that. If you leave it out: The change lasts until you delete it. |
| `overrides.reason` | string | optional | Why you made this change, in your own words. QuotaStack stores it and shows it back to you. If you leave it out: QuotaStack stores an empty string. |

### Errors

- `401` — Missing or invalid authentication.
- `404` — No [subscription](/docs/concepts/subscriptions) here has this `id`. You get the same reply when the subscription belongs to the other environment. A sandbox key asking about a live subscription is told it does not exist. See [not-found](/docs/api/errors/not-found).

### See also

- [Entitlements](/docs/concepts/entitlements)
- [Subscriptions](/docs/api/subscriptions)

## Create an override

`POST /v1/subscriptions/{id}/overrides`

- **Idempotency-Key:** Required. Returns 422 without it.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** One override row, and a fresh [entitlement](/docs/concepts/entitlements) cache for that customer. The customer sees the new value on their very next check.
- **Fires:** No webhooks.

This call changes one [entitlement](/docs/concepts/entitlements) for one [subscription](/docs/concepts/subscriptions). The [plan](/docs/concepts/plans-and-variants) stays as it is, and everyone else on it keeps the old value. Boolean, gauge, and static metrics can be changed this way. A metered metric cannot. QuotaStack returns `400` for one of those. Change a metered price with a [metering rule](/docs/concepts/metering) instead. One [subscription](/docs/concepts/subscriptions) can hold one override per metric. A second one for the same metric returns `409`. An override that already expired is the exception. QuotaStack deletes the old one and makes the new one, and you get a new `id`.

### Header parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `Idempotency-Key` | string | required | A unique string you choose, up to 256 characters. It stops a retry from doing the work twice. This header is required on every POST and PATCH. Leave it out and the request fails with `422`, before any change is made. Send the same key twice and QuotaStack replays the first response. The second call's status and body match the first, and the response carries `X-Idempotent-Replayed: true`. Replays are available for 24 hours. Derive the key from the business event rather than a random value, so a retry from anywhere reuses it — for example `topup:{payment_intent_id}` or `renewal:{subscription_id}:{period_start}`. |

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required |  |

### Body parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `billable_metric_key` | string | required | Which [billable metric](/docs/concepts/metering) this change is about. If you leave it out: QuotaStack rejects the call with `400`. This field is required. |
| `value` | object | required | The new value, shaped to match the metric type. A boolean metric takes `{"enabled": true}`. A gauge metric takes `{"cap": 250}`. A static metric takes `{"config": {...}}`. QuotaStack checks the shape against the metric, and returns `400` when the two disagree. If you leave it out: QuotaStack rejects the call with `400`. This field is required. |
| `expires_at` | string (date-time) | optional | When this change stops applying. The [plan](/docs/concepts/plans-and-variants) value takes over again after that. If you leave it out: The change lasts until you delete it. |
| `reason` | string | optional | Why you made this change, in your own words. QuotaStack stores it and shows it back to you. If you leave it out: QuotaStack stores an empty string. |

### Request

```json
{
  "billable_metric_key": "seats",
  "value": {
    "cap": 25
  },
  "reason": "Enterprise trial, agreed with sales"
}
```

### Response 201

```json
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a73",
  "subscription_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a21",
  "billable_metric_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a64",
  "billable_metric_key": "seats",
  "value": {
    "cap": 25
  },
  "expires_at": null,
  "reason": "Enterprise trial, agreed with sales",
  "created_at": "2026-07-28T09:00:00Z",
  "updated_at": "2026-07-28T09:00:00Z"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required |  |
| `tenant_id` | string (uuid) | optional |  |
| `subscription_id` | string (uuid) | required | Which [subscription](/docs/concepts/subscriptions) this change belongs to. One override covers one subscription, and no other. |
| `billable_metric_id` | string (uuid) | required | QuotaStack's own ID for that metric. Match on `billable_metric_key` instead; the key is the one you chose. |
| `billable_metric_key` | string | optional | Which [billable metric](/docs/concepts/metering) this change is about. |
| `value` | object | required | The new value, shaped to match the metric type. A boolean metric takes `{"enabled": true}`. A gauge metric takes `{"cap": 250}`. A static metric takes `{"config": {...}}`. QuotaStack checks the shape against the metric, and returns `400` when the two disagree. |
| `expires_at` | string (date-time) | optional | When this change stops applying. The [plan](/docs/concepts/plans-and-variants) value takes over again after that. If you leave it out: The change lasts until you delete it. |
| `reason` | string | optional | Why you made this change, in your own words. QuotaStack stores it and shows it back to you. If you leave it out: QuotaStack stores an empty string. |
| `created_at` | string (date-time) | required |  |
| `updated_at` | string (date-time) | required |  |

### Errors

- `400` — The metric is metered, which this call cannot change. Or `value` does not match the metric type. A gauge needs `cap`, and a boolean needs `enabled`. See [bad-request](/docs/api/errors/bad-request).
- `401` — Missing or invalid authentication.
- `404` — No [subscription](/docs/concepts/subscriptions) here has this `id`. You get the same reply when the subscription belongs to the other environment. A sandbox key asking about a live subscription is told it does not exist. See [not-found](/docs/api/errors/not-found).
- `409` — This [subscription](/docs/concepts/subscriptions) already has a live override for that metric. Update it, or delete it first. See [conflict](/docs/api/errors/conflict).

### See also

- [Entitlements](/docs/concepts/entitlements)
- [Entitlements](/docs/api/entitlements)
- [Plans](/docs/api/plans)

## Update an override

`PATCH /v1/subscriptions/{id}/overrides/{billable_metric_key}`

- **Idempotency-Key:** Required. Returns 422 without it.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** The override row, and a fresh [entitlement](/docs/concepts/entitlements) cache for that customer.
- **Fires:** No webhooks.

This call changes an override you already made. The path takes the metric key, not the override `id`. Send only the fields you want changed. The rest stay as they are. `expires_at` works in a way the other fields do not. Send `null` for it and the override becomes permanent. Leave the field out and the old expiry stays. A new `value` is checked against the metric type, the same as on create.

### Header parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `Idempotency-Key` | string | required | A unique string you choose, up to 256 characters. It stops a retry from doing the work twice. This header is required on every POST and PATCH. Leave it out and the request fails with `422`, before any change is made. Send the same key twice and QuotaStack replays the first response. The second call's status and body match the first, and the response carries `X-Idempotent-Replayed: true`. Replays are available for 24 hours. Derive the key from the business event rather than a random value, so a retry from anywhere reuses it — for example `topup:{payment_intent_id}` or `renewal:{subscription_id}:{period_start}`. |

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | 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 |
|---|---|---|---|
| `value` | object | optional | The new value, shaped to match the metric type. A boolean metric takes `{"enabled": true}`. A gauge metric takes `{"cap": 250}`. A static metric takes `{"config": {...}}`. QuotaStack checks the shape against the metric, and returns `400` when the two disagree. If you leave it out: The value stays as it was. |
| `expires_at` | string (date-time) | optional | A new end date for this change. If you leave it out: The old expiry stays. Send `null` to drop the expiry and make the override permanent. This field is the only one here that reads `null` and an absent key as two different things. |
| `reason` | string | optional | Why you made this change, in your own words. QuotaStack stores it and shows it back to you. If you leave it out: The old reason stays. |

### Request

```json
{
  "value": {
    "cap": 50
  }
}
```

### Response 200

```json
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a73",
  "subscription_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a21",
  "billable_metric_id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a64",
  "billable_metric_key": "seats",
  "value": {
    "cap": 50
  },
  "expires_at": null,
  "reason": "Enterprise trial, agreed with sales",
  "created_at": "2026-07-28T09:00:00Z",
  "updated_at": "2026-07-28T11:00:00Z"
}
```

### Response fields

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required |  |
| `tenant_id` | string (uuid) | optional |  |
| `subscription_id` | string (uuid) | required | Which [subscription](/docs/concepts/subscriptions) this change belongs to. One override covers one subscription, and no other. |
| `billable_metric_id` | string (uuid) | required | QuotaStack's own ID for that metric. Match on `billable_metric_key` instead; the key is the one you chose. |
| `billable_metric_key` | string | optional | Which [billable metric](/docs/concepts/metering) this change is about. |
| `value` | object | required | The new value, shaped to match the metric type. A boolean metric takes `{"enabled": true}`. A gauge metric takes `{"cap": 250}`. A static metric takes `{"config": {...}}`. QuotaStack checks the shape against the metric, and returns `400` when the two disagree. |
| `expires_at` | string (date-time) | optional | When this change stops applying. The [plan](/docs/concepts/plans-and-variants) value takes over again after that. If you leave it out: The change lasts until you delete it. |
| `reason` | string | optional | Why you made this change, in your own words. QuotaStack stores it and shows it back to you. If you leave it out: QuotaStack stores an empty string. |
| `created_at` | string (date-time) | required |  |
| `updated_at` | string (date-time) | required |  |

### Errors

- `400` — `value` does not match the metric type. A gauge needs `cap`, and a boolean needs `enabled`. See [bad-request](/docs/api/errors/bad-request).
- `401` — Missing or invalid authentication.
- `404` — No override here covers that metric on that [subscription](/docs/concepts/subscriptions). The subscription may be missing too, or set up in the other environment. See [not-found](/docs/api/errors/not-found).

### See also

- [Entitlements](/docs/concepts/entitlements)
- [Entitlements](/docs/api/entitlements)

## Delete an override

`DELETE /v1/subscriptions/{id}/overrides/{billable_metric_key}`

- **Idempotency-Key:** Required. Returns 422 without it.
- **Environment:** Sandbox and Live
- **Auth:** Tenant API key
- **Writes:** Marks the override deleted, and clears that customer [entitlement](/docs/concepts/entitlements) cache. No credits move, and the [subscription](/docs/concepts/subscriptions) itself is untouched.
- **Fires:** No webhooks.

This call ends the special treatment. The customer goes back to the [plan](/docs/concepts/plans-and-variants) value right away. QuotaStack keeps the row and marks it deleted, so the history stays. The reply carries no body. A success is `204` and nothing else. You can make a new override for the same metric afterwards.

### Header parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `Idempotency-Key` | string | required | A unique string you choose, up to 256 characters. It stops a retry from doing the work twice. This header is required on every POST and PATCH. Leave it out and the request fails with `422`, before any change is made. Send the same key twice and QuotaStack replays the first response. The second call's status and body match the first, and the response carries `X-Idempotent-Replayed: true`. Replays are available for 24 hours. Derive the key from the business event rather than a random value, so a retry from anywhere reuses it — for example `topup:{payment_intent_id}` or `renewal:{subscription_id}:{period_start}`. |

### Path parameters

| Field | Type | Required | Meaning |
|---|---|---|---|
| `id` | string (uuid) | required |  |
| `billable_metric_key` | string | required | The [billable metric](/docs/concepts/metering)’s own key. Not a QuotaStack id. |

### Errors

- `401` — Missing or invalid authentication.
- `404` — No override here covers that metric on that [subscription](/docs/concepts/subscriptions). The subscription may sit in the other environment. See [not-found](/docs/api/errors/not-found).

### See also

- [Entitlements](/docs/concepts/entitlements)
- [Entitlements](/docs/api/entitlements)
