quotastack Docs
Docs / API / Customers

Customers

The people who use your product.

A customer is one person or account that uses your app. QuotaStack keeps one record per customer, in every environment.

Every credit balance, a subscription, and an entitlement check all read from this one record.

You can look up a customer by QuotaStack’s own ID or by your own ID. Most calls take either one.

Which call do I want?

8 of 8 operations documented in full.

List customers (paginated).

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

This endpoint lists every customer, newest first. Add search to match part of external_id or display_name.

Query parameters
FieldTypeMeaning
searchoptionalstring

Only customers whose external_id or display_name has this text.

If you leave it out: You get the full list back.

cursoroptionalstring

Opaque pagination cursor from the previous page's pagination.next_cursor. Omit to start at the first page.

limitoptionalinteger

Page size. Default 20, max 100.

Response 200
{
  "data": [
    {
      "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
      "external_id": "user_42",
      "environment": "live",
      "display_name": "Ada Lovelace",
      "overage_policy": null,
      "low_balance_threshold_mc": null,
      "metadata": {},
      "created_at": "2026-07-20T09:00:00Z",
      "updated_at": "2026-07-20T09:00:00Z"
    }
  ],
  "pagination": {
    "has_more": false,
    "next_cursor": null
  }
}
Response fields
FieldTypeMeaning
datarequiredarray

One row per customer, newest first.

paginationrequiredobject

Cursor-based pagination envelope. Returned on every list endpoint. No total — cursor-only.

Errors

  • 400The cursor value is not one QuotaStack issued. Omit it to start from the first page. bad-request
  • 401Missing or invalid authentication.
  • 429Rate limit exceeded.
  • 500Unexpected server error.

Create a customer.

POST/v1/customers
Idempotency-KeyRequired. Returns 422 without it.
EnvironmentSandbox and Live
AuthTenant API key
WritesOne new customer record. Nothing else changes.
Fires No webhooks.

Creating a customer makes one record, nothing else. QuotaStack grants no credits and starts no subscription. Do both as separate calls. Send your own external_id so later calls can use it instead of the QuotaStack UUID.

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}.

Body parameters
FieldTypeMeaning
external_idrequiredstring

Your own ID for this customer. Must be unique for your tenant.

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

display_nameoptionalstring

A label people see in the dashboard. Up to 200 characters.

If you leave it out: QuotaStack stores null. The dashboard shows the external_id instead.

overage_policyoptionalstring

The customer's own value wins when set. The tenant default overage rule applies when it is not.

If you leave it out: Left out, QuotaStack stores null.

  • blockRefuse usage once the balance runs out.
  • allowLet usage continue past the balance. QuotaStack floors it at zero and logs the rest as overage.
  • notifyThe same as allow. No webhook fires for the overage yet.
low_balance_threshold_mcoptionalinteger (int64)0–∞

Turns on the credit.low_balance webhook at this amount, for this customer only. Zero turns it off.

Unit: millicredits, where 1 credit is 1000 millicredits.

If you leave it out: Left out, QuotaStack uses the tenant default.

metadataoptionalobject

Your own key-value tags. QuotaStack stores them unchanged and returns them on every read.

If you leave it out: Left out, QuotaStack stores {}.

Request
{
  "external_id": "user_42",
  "display_name": "Ada Lovelace"
}
Response 201
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "external_id": "user_42",
  "environment": "live",
  "display_name": "Ada Lovelace",
  "overage_policy": null,
  "low_balance_threshold_mc": null,
  "metadata": {},
  "created_at": "2026-07-20T09:00:00Z",
  "updated_at": "2026-07-20T09:00:00Z"
}
Response fields
FieldTypeMeaning
idrequiredstring (uuid)

QuotaStack-assigned customer UUID.

tenant_idrequiredstring (uuid)
environmentrequiredstring
  • live
  • sandbox

Which environment this resource lives in. Determined by the API key prefix used (qs_live_… → live, qs_test_… → sandbox).

external_idrequiredstring

Your own ID for this customer, set when you created it.

display_nameoptionalstring

A label people see in the dashboard. null until you set one.

overage_policyoptionalstring

The value stored for this customer. null means none is set; the tenant default overage rule applies.

  • blockRefuse usage once the balance runs out.
  • allowLet usage continue past the balance. QuotaStack floors it at zero and logs the rest as overage.
  • notifyThe same as allow. No webhook fires for the overage yet.
low_balance_threshold_mcoptionalinteger (int64)0–∞

The credit.low_balance webhook threshold for this customer. null means the tenant default applies.

Unit: millicredits, where 1 credit is 1000 millicredits.

metadatarequiredobject

Your own key-value tags, unchanged since the last write.

created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)

Errors

  • 400QuotaStack could not parse the request body as JSON. bad-request
  • 401Missing or invalid authentication.
  • 409The external_id is taken. Fetch that customer instead of making a new one. conflict
  • 422external_id is missing, display_name is over 200 characters, or low_balance_threshold_mc is negative. The response names the field. validation-error
  • 429Rate limit exceeded.
  • 500Unexpected server error.

Retrieve a customer by QuotaStack id.

GET/v1/customers/{id}
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 finds one customer. Your own external_id, or the QuotaStack UUID. QuotaStack tries external_id first, then the UUID. An unknown id never creates a customer here, unlike a credit grant. QuotaStack returns 404 instead.

Path parameters
FieldTypeMeaning
idrequiredstring (uuid)

Your own external_id, or the QuotaStack UUID. QuotaStack tries external_id first, then the UUID.

Response 200
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "external_id": "user_42",
  "environment": "live",
  "display_name": "Ada Lovelace",
  "overage_policy": null,
  "low_balance_threshold_mc": null,
  "metadata": {},
  "created_at": "2026-07-20T09:00:00Z",
  "updated_at": "2026-07-20T09:00:00Z"
}
Response fields
FieldTypeMeaning
idrequiredstring (uuid)

QuotaStack-assigned customer UUID.

tenant_idrequiredstring (uuid)
environmentrequiredstring
  • live
  • sandbox

Which environment this resource lives in. Determined by the API key prefix used (qs_live_… → live, qs_test_… → sandbox).

external_idrequiredstring

Your own ID for this customer, set when you created it.

display_nameoptionalstring

A label people see in the dashboard. null until you set one.

overage_policyoptionalstring

The value stored for this customer. null means none is set; the tenant default overage rule applies.

  • blockRefuse usage once the balance runs out.
  • allowLet usage continue past the balance. QuotaStack floors it at zero and logs the rest as overage.
  • notifyThe same as allow. No webhook fires for the overage yet.
low_balance_threshold_mcoptionalinteger (int64)0–∞

The credit.low_balance webhook threshold for this customer. null means the tenant default applies.

Unit: millicredits, where 1 credit is 1000 millicredits.

metadatarequiredobject

Your own key-value tags, unchanged since the last write.

created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)

Errors

  • 401Missing or invalid authentication.
  • 404No customer here has this id. The customer may already be deleted. not-found
  • 429Rate limit exceeded.
  • 500Unexpected server error.

Update a customer by QuotaStack id.

PATCH/v1/customers/{id}
Idempotency-KeyRequired. Returns 422 without it.
EnvironmentSandbox and Live
AuthTenant API key
WritesReplaces display_name, overage_policy, and low_balance_threshold_mc with whatever you send. Merges metadata into what is already there, one key at a time. QuotaStack never removes a key you leave out.
Fires No webhooks.

This call changes one or more fields. A field you skip keeps its old value. Your own external_id, or the QuotaStack UUID. QuotaStack tries external_id first, then the UUID.

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)

Your own external_id, or the QuotaStack UUID. QuotaStack tries external_id first, then the UUID.

Body parameters
FieldTypeMeaning
display_nameoptionalstring

A label people see in the dashboard, up to 200 characters. Send an empty string to clear it.

If you leave it out: The old value stays. QuotaStack changes nothing.

overage_policyoptionalstring

The customer's own value wins when set. The tenant default overage rule applies when it is not. Send an empty string to clear it and fall back to the tenant default.

If you leave it out: The old value stays. QuotaStack changes nothing.

  • blockRefuse usage once the balance runs out.
  • allowLet usage continue past the balance. QuotaStack floors it at zero and logs the rest as overage.
  • notifyThe same as allow. No webhook fires for the overage yet.
low_balance_threshold_mcoptionalinteger (int64)0–∞

Turns on the credit.low_balance webhook at this amount, for this customer only. Zero turns it off.

Unit: millicredits, where 1 credit is 1000 millicredits.

If you leave it out: The old value stays. QuotaStack changes nothing.

metadataoptionalobject

Your own key-value tags. QuotaStack merges this into what is already there, one key at a time. A key you send overwrites its old value. A key you leave out keeps its old value.

If you leave it out: The old value stays. QuotaStack changes nothing. There is no way to remove a key through this endpoint.

Request
{
  "display_name": "Ada Lovelace"
}
Response 200
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "external_id": "user_42",
  "environment": "live",
  "display_name": "Ada Lovelace",
  "overage_policy": null,
  "low_balance_threshold_mc": null,
  "metadata": {},
  "created_at": "2026-07-20T09:00:00Z",
  "updated_at": "2026-07-20T09:00:00Z"
}
Response fields
FieldTypeMeaning
idrequiredstring (uuid)

QuotaStack-assigned customer UUID.

tenant_idrequiredstring (uuid)
environmentrequiredstring
  • live
  • sandbox

Which environment this resource lives in. Determined by the API key prefix used (qs_live_… → live, qs_test_… → sandbox).

external_idrequiredstring

Your own ID for this customer, set when you created it.

display_nameoptionalstring

A label people see in the dashboard. null until you set one.

overage_policyoptionalstring

The value stored for this customer. null means none is set; the tenant default overage rule applies.

  • blockRefuse usage once the balance runs out.
  • allowLet usage continue past the balance. QuotaStack floors it at zero and logs the rest as overage.
  • notifyThe same as allow. No webhook fires for the overage yet.
low_balance_threshold_mcoptionalinteger (int64)0–∞

The credit.low_balance webhook threshold for this customer. null means the tenant default applies.

Unit: millicredits, where 1 credit is 1000 millicredits.

metadatarequiredobject

Your own key-value tags, unchanged since the last write.

created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)

Errors

  • 400QuotaStack could not parse the request body as JSON. bad-request
  • 401Missing or invalid authentication.
  • 404No customer here has this id. The customer may already be deleted. not-found
  • 422display_name is over 200 characters, or low_balance_threshold_mc is negative. The response names the field. validation-error
  • 429Rate limit exceeded.
  • 500Unexpected server error.

Soft-delete a customer by QuotaStack id.

DELETE/v1/customers/{id}
Idempotency-KeyNot used. This is a read.
EnvironmentSandbox and Live
AuthTenant API key
WritesSets deleted_at on the customer. Nothing more. The credit account stays. Active subscriptions stay. Open reservations stay. Ledger history stays. To wind the customer down fully, cancel the subscription and zero the balance yourself.
Fires No webhooks.

A delete here is soft. QuotaStack sets deleted_at and nothing else. Every later read skips this customer. A second delete on the same id returns 404, not a second success. Your own external_id, or the QuotaStack UUID. QuotaStack tries external_id first, then the UUID. You can later make a new customer with the same external_id. QuotaStack treats it as a different record, with a new id.

Path parameters
FieldTypeMeaning
idrequiredstring (uuid)

Your own external_id, or the QuotaStack UUID. QuotaStack tries external_id first, then the UUID.

Errors

  • 401Missing or invalid authentication.
  • 404No customer here has this id. The customer may already be deleted. not-found
  • 429Rate limit exceeded.
  • 500Unexpected server error.

Retrieve a customer by your external_id.

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

The external_id twin of getCustomer. Only your own external_id matches here, never a QuotaStack UUID.

Path parameters
FieldTypeMeaning
external_idrequiredstring

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
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "external_id": "user_42",
  "environment": "live",
  "display_name": "Ada Lovelace",
  "overage_policy": null,
  "low_balance_threshold_mc": null,
  "metadata": {},
  "created_at": "2026-07-20T09:00:00Z",
  "updated_at": "2026-07-20T09:00:00Z"
}
Response fields
FieldTypeMeaning
idrequiredstring (uuid)

QuotaStack-assigned customer UUID.

tenant_idrequiredstring (uuid)
environmentrequiredstring
  • live
  • sandbox

Which environment this resource lives in. Determined by the API key prefix used (qs_live_… → live, qs_test_… → sandbox).

external_idrequiredstring

Your own ID for this customer, set when you created it.

display_nameoptionalstring

A label people see in the dashboard. null until you set one.

overage_policyoptionalstring

The value stored for this customer. null means none is set; the tenant default overage rule applies.

  • blockRefuse usage once the balance runs out.
  • allowLet usage continue past the balance. QuotaStack floors it at zero and logs the rest as overage.
  • notifyThe same as allow. No webhook fires for the overage yet.
low_balance_threshold_mcoptionalinteger (int64)0–∞

The credit.low_balance webhook threshold for this customer. null means the tenant default applies.

Unit: millicredits, where 1 credit is 1000 millicredits.

metadatarequiredobject

Your own key-value tags, unchanged since the last write.

created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)

Errors

  • 401Missing or invalid authentication.
  • 404No customer here has this external_id. The customer may already be deleted. not-found
  • 429Rate limit exceeded.
  • 500Unexpected server error.

Update a customer by your external_id.

PATCH/v1/customer-by-external-id/{external_id}
Idempotency-KeyRequired. Returns 422 without it.
EnvironmentSandbox and Live
AuthTenant API key
WritesReplaces display_name, overage_policy, and low_balance_threshold_mc with whatever you send. Merges metadata into what is already there, one key at a time. QuotaStack never removes a key you leave out.
Fires No webhooks.

The external_id twin of updateCustomer. Only your own external_id matches here, never a QuotaStack UUID. A field you skip keeps its old value.

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
external_idrequiredstring

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.

Body parameters
FieldTypeMeaning
display_nameoptionalstring

A label people see in the dashboard, up to 200 characters. Send an empty string to clear it.

If you leave it out: The old value stays. QuotaStack changes nothing.

overage_policyoptionalstring

The customer's own value wins when set. The tenant default overage rule applies when it is not. Send an empty string to clear it and fall back to the tenant default.

If you leave it out: The old value stays. QuotaStack changes nothing.

  • blockRefuse usage once the balance runs out.
  • allowLet usage continue past the balance. QuotaStack floors it at zero and logs the rest as overage.
  • notifyThe same as allow. No webhook fires for the overage yet.
low_balance_threshold_mcoptionalinteger (int64)0–∞

Turns on the credit.low_balance webhook at this amount, for this customer only. Zero turns it off.

Unit: millicredits, where 1 credit is 1000 millicredits.

If you leave it out: The old value stays. QuotaStack changes nothing.

metadataoptionalobject

Your own key-value tags. QuotaStack merges this into what is already there, one key at a time. A key you send overwrites its old value. A key you leave out keeps its old value.

If you leave it out: The old value stays. QuotaStack changes nothing. There is no way to remove a key through this endpoint.

Request
{
  "display_name": "Ada Lovelace"
}
Response 200
{
  "id": "0192f5a4-7c31-7b8e-9a2d-4f6c8e1b3a05",
  "external_id": "user_42",
  "environment": "live",
  "display_name": "Ada Lovelace",
  "overage_policy": null,
  "low_balance_threshold_mc": null,
  "metadata": {},
  "created_at": "2026-07-20T09:00:00Z",
  "updated_at": "2026-07-20T09:00:00Z"
}
Response fields
FieldTypeMeaning
idrequiredstring (uuid)

QuotaStack-assigned customer UUID.

tenant_idrequiredstring (uuid)
environmentrequiredstring
  • live
  • sandbox

Which environment this resource lives in. Determined by the API key prefix used (qs_live_… → live, qs_test_… → sandbox).

external_idrequiredstring

Your own ID for this customer, set when you created it.

display_nameoptionalstring

A label people see in the dashboard. null until you set one.

overage_policyoptionalstring

The value stored for this customer. null means none is set; the tenant default overage rule applies.

  • blockRefuse usage once the balance runs out.
  • allowLet usage continue past the balance. QuotaStack floors it at zero and logs the rest as overage.
  • notifyThe same as allow. No webhook fires for the overage yet.
low_balance_threshold_mcoptionalinteger (int64)0–∞

The credit.low_balance webhook threshold for this customer. null means the tenant default applies.

Unit: millicredits, where 1 credit is 1000 millicredits.

metadatarequiredobject

Your own key-value tags, unchanged since the last write.

created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)

Errors

  • 400QuotaStack could not parse the request body as JSON. bad-request
  • 401Missing or invalid authentication.
  • 404No customer here has this external_id. The customer may already be deleted. not-found
  • 422display_name is over 200 characters, or low_balance_threshold_mc is negative. The response names the field. validation-error
  • 429Rate limit exceeded.
  • 500Unexpected server error.

Soft-delete a customer by your external_id.

DELETE/v1/customer-by-external-id/{external_id}
Idempotency-KeyNot used. This is a read.
EnvironmentSandbox and Live
AuthTenant API key
WritesSets deleted_at on the customer. Nothing more. The credit account stays. Active subscriptions stay. Open reservations stay. Ledger history stays.
Fires No webhooks.

The external_id twin of deleteCustomer. Only your own external_id matches here, never a QuotaStack UUID. A second delete on the same external_id returns 404, not a second success. You can later make a new customer with the same external_id. QuotaStack treats it as a different record, with a new id.

Path parameters
FieldTypeMeaning
external_idrequiredstring

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.

Errors

  • 401Missing or invalid authentication.
  • 404No customer here has this external_id. The customer may already be deleted. not-found
  • 429Rate limit exceeded.
  • 500Unexpected server error.