quotastack Docs
Docs / API / Overrides

Overrides

Change one customer without a new plan.

An override changes what a single customer gets. The plan 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, 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 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 instead, and move those customers onto it.

Which call do I want?

4 of 4 operations documented in full.

List overrides

GET/v1/subscriptions/{id}/overrides
Idempotency-KeyNot used. This is a read.
EnvironmentSandbox and Live
AuthTenant API key
WritesNothing. A read has no side effects.
Fires No webhooks.

This call returns every live override on one subscription. 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
FieldTypeMeaning
idrequiredstring (uuid)
Response 200
{
  "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
FieldTypeMeaning
overridesoptionalarray
overrides.subscription_idrequiredstring (uuid)

Which subscription this change belongs to. One override covers one subscription, and no other.

overrides.billable_metric_idrequiredstring (uuid)

QuotaStack's own ID for that metric. Match on billable_metric_key instead; the key is the one you chose.

overrides.billable_metric_keyoptionalstring

Which billable metric this change is about.

overrides.valuerequiredobject

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_atoptionalstring (date-time)

When this change stops applying. The plan value takes over again after that.

If you leave it out: The change lasts until you delete it.

overrides.reasonoptionalstring

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

  • 401Missing or invalid authentication.
  • 404No subscription 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. not-found

Create an override

POST/v1/subscriptions/{id}/overrides
Idempotency-KeyRequired. Returns 422 without it.
EnvironmentSandbox and Live
AuthTenant API key
WritesOne override row, and a fresh entitlement cache for that customer. The customer sees the new value on their very next check.
Fires No webhooks.

This call changes one entitlement for one subscription. The plan 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 instead. One subscription 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
FieldTypeMeaning
Idempotency-Keyrequiredstring

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
FieldTypeMeaning
idrequiredstring (uuid)
Body parameters
FieldTypeMeaning
billable_metric_keyrequiredstring

Which billable metric this change is about.

If you leave it out: QuotaStack rejects the call with 400. This field is required.

valuerequiredobject

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_atoptionalstring (date-time)

When this change stops applying. The plan value takes over again after that.

If you leave it out: The change lasts until you delete it.

reasonoptionalstring

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
{
  "billable_metric_key": "seats",
  "value": {
    "cap": 25
  },
  "reason": "Enterprise trial, agreed with sales"
}
Response 201
{
  "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
FieldTypeMeaning
idrequiredstring (uuid)
tenant_idoptionalstring (uuid)
subscription_idrequiredstring (uuid)

Which subscription this change belongs to. One override covers one subscription, and no other.

billable_metric_idrequiredstring (uuid)

QuotaStack's own ID for that metric. Match on billable_metric_key instead; the key is the one you chose.

billable_metric_keyoptionalstring

Which billable metric this change is about.

valuerequiredobject

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_atoptionalstring (date-time)

When this change stops applying. The plan value takes over again after that.

If you leave it out: The change lasts until you delete it.

reasonoptionalstring

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_atrequiredstring (date-time)
updated_atrequiredstring (date-time)

Errors

  • 400The 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. bad-request
  • 401Missing or invalid authentication.
  • 404No subscription 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. not-found
  • 409This subscription already has a live override for that metric. Update it, or delete it first. conflict

Update an override

PATCH/v1/subscriptions/{id}/overrides/{billable_metric_key}
Idempotency-KeyRequired. Returns 422 without it.
EnvironmentSandbox and Live
AuthTenant API key
WritesThe override row, and a fresh entitlement 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
FieldTypeMeaning
Idempotency-Keyrequiredstring

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
FieldTypeMeaning
idrequiredstring (uuid)
billable_metric_keyrequiredstring

The billable metric’s own key. Not a QuotaStack id.

Body parameters
FieldTypeMeaning
valueoptionalobject

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_atoptionalstring (date-time)

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.

reasonoptionalstring

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
{
  "value": {
    "cap": 50
  }
}
Response 200
{
  "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
FieldTypeMeaning
idrequiredstring (uuid)
tenant_idoptionalstring (uuid)
subscription_idrequiredstring (uuid)

Which subscription this change belongs to. One override covers one subscription, and no other.

billable_metric_idrequiredstring (uuid)

QuotaStack's own ID for that metric. Match on billable_metric_key instead; the key is the one you chose.

billable_metric_keyoptionalstring

Which billable metric this change is about.

valuerequiredobject

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_atoptionalstring (date-time)

When this change stops applying. The plan value takes over again after that.

If you leave it out: The change lasts until you delete it.

reasonoptionalstring

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_atrequiredstring (date-time)
updated_atrequiredstring (date-time)

Errors

  • 400value does not match the metric type. A gauge needs cap, and a boolean needs enabled. bad-request
  • 401Missing or invalid authentication.
  • 404No override here covers that metric on that subscription. The subscription may be missing too, or set up in the other environment. not-found

Delete an override

DELETE/v1/subscriptions/{id}/overrides/{billable_metric_key}
Idempotency-KeyRequired. Returns 422 without it.
EnvironmentSandbox and Live
AuthTenant API key
WritesMarks the override deleted, and clears that customer entitlement cache. No credits move, and the subscription itself is untouched.
Fires No webhooks.

This call ends the special treatment. The customer goes back to the plan 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
FieldTypeMeaning
Idempotency-Keyrequiredstring

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
FieldTypeMeaning
idrequiredstring (uuid)
billable_metric_keyrequiredstring

The billable metric’s own key. Not a QuotaStack id.

Errors

  • 401Missing or invalid authentication.
  • 404No override here covers that metric on that subscription. The subscription may sit in the other environment. not-found