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?
- You are creating a customer for the first time →
POST /v1/customers - You have your own user ID and want the customer →
GET /v1/customer-by-external-id/{external_id} - You have QuotaStack's customer ID and want the customer →
GET /v1/customers/{id}Store theexternal_idyou already use for that user. Then you never need to look up the QuotaStack ID at all.
List customers (paginated).
/v1/customers | Idempotency-Key | Not used. This is a read. |
|---|---|
| Environment | Sandbox and Live |
| Auth | Tenant API key |
| Writes | Nothing. A read has no side effects. |
| Fires | No webhooks. |
This endpoint lists every customer, newest first. Add search to match part of external_id or display_name.
| Field | Type | Meaning |
|---|---|---|
searchoptional | string | Only customers whose If you leave it out: You get the full list back. |
cursoroptional | string | Opaque pagination cursor from the previous page's |
limitoptional | integer | Page size. Default 20, max 100. |
{
"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
}
} | Field | Type | Meaning |
|---|---|---|
datarequired | array | One row per customer, newest first. |
paginationrequired | object | Cursor-based pagination envelope. Returned on every list endpoint. No |
Errors
400— Thecursorvalue is not one QuotaStack issued. Omit it to start from the first page. bad-request401— Missing or invalid authentication.429— Rate limit exceeded.500— Unexpected server error.
See also
Create a customer.
/v1/customers | Idempotency-Key | Required. Returns 422 without it. |
|---|---|
| Environment | Sandbox and Live |
| Auth | Tenant API key |
| Writes | One 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.
| Field | Type | Meaning |
|---|---|---|
Idempotency-Keyrequired | string | 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 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 Derive the key from the business event rather than a random value, so a retry from anywhere reuses it — for example |
| Field | Type | Meaning |
|---|---|---|
external_idrequired | string | 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_nameoptional | string | A label people see in the dashboard. Up to 200 characters. If you leave it out: QuotaStack stores |
overage_policyoptional | string | 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 |
low_balance_threshold_mcoptional | integer (int64)0–∞ | Turns on the Unit: millicredits, where 1 credit is 1000 millicredits. If you leave it out: Left out, QuotaStack uses the tenant default. |
metadataoptional | object | Your own key-value tags. QuotaStack stores them unchanged and returns them on every read. If you leave it out: Left out, QuotaStack stores |
{
"external_id": "user_42",
"display_name": "Ada Lovelace"
} {
"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"
} | Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | QuotaStack-assigned customer UUID. |
tenant_idrequired | string (uuid) | |
environmentrequired | string |
Which environment this resource lives in. Determined by the API key prefix used ( |
external_idrequired | string | Your own ID for this customer, set when you created it. |
display_nameoptional | string | A label people see in the dashboard. |
overage_policyoptional | string | The value stored for this customer. |
low_balance_threshold_mcoptional | integer (int64)0–∞ | The Unit: millicredits, where 1 credit is 1000 millicredits. |
metadatarequired | object | Your own key-value tags, unchanged since the last write. |
created_atrequired | string (date-time) | |
updated_atrequired | string (date-time) |
Errors
400— QuotaStack could not parse the request body as JSON. bad-request401— Missing or invalid authentication.409— Theexternal_idis taken. Fetch that customer instead of making a new one. conflict422—external_idis missing,display_nameis over 200 characters, orlow_balance_threshold_mcis negative. The response names the field. validation-error429— Rate limit exceeded.500— Unexpected server error.
Retrieve a customer by QuotaStack id.
/v1/customers/{id} | 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 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.
| Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | Your own |
{
"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"
} | Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | QuotaStack-assigned customer UUID. |
tenant_idrequired | string (uuid) | |
environmentrequired | string |
Which environment this resource lives in. Determined by the API key prefix used ( |
external_idrequired | string | Your own ID for this customer, set when you created it. |
display_nameoptional | string | A label people see in the dashboard. |
overage_policyoptional | string | The value stored for this customer. |
low_balance_threshold_mcoptional | integer (int64)0–∞ | The Unit: millicredits, where 1 credit is 1000 millicredits. |
metadatarequired | object | Your own key-value tags, unchanged since the last write. |
created_atrequired | string (date-time) | |
updated_atrequired | string (date-time) |
Errors
401— Missing or invalid authentication.404— No customer here has thisid. The customer may already be deleted. not-found429— Rate limit exceeded.500— Unexpected server error.
See also
Update a customer by QuotaStack id.
/v1/customers/{id} | Idempotency-Key | Required. Returns 422 without it. |
|---|---|
| Environment | Sandbox and Live |
| Auth | Tenant API key |
| Writes | Replaces 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.
| Field | Type | Meaning |
|---|---|---|
Idempotency-Keyrequired | string | 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 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 Derive the key from the business event rather than a random value, so a retry from anywhere reuses it — for example |
| Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | Your own |
| Field | Type | Meaning |
|---|---|---|
display_nameoptional | string | 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_policyoptional | string | 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. |
low_balance_threshold_mcoptional | integer (int64)0–∞ | Turns on the Unit: millicredits, where 1 credit is 1000 millicredits. If you leave it out: The old value stays. QuotaStack changes nothing. |
metadataoptional | object | 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. |
{
"display_name": "Ada Lovelace"
} {
"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"
} | Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | QuotaStack-assigned customer UUID. |
tenant_idrequired | string (uuid) | |
environmentrequired | string |
Which environment this resource lives in. Determined by the API key prefix used ( |
external_idrequired | string | Your own ID for this customer, set when you created it. |
display_nameoptional | string | A label people see in the dashboard. |
overage_policyoptional | string | The value stored for this customer. |
low_balance_threshold_mcoptional | integer (int64)0–∞ | The Unit: millicredits, where 1 credit is 1000 millicredits. |
metadatarequired | object | Your own key-value tags, unchanged since the last write. |
created_atrequired | string (date-time) | |
updated_atrequired | string (date-time) |
Errors
400— QuotaStack could not parse the request body as JSON. bad-request401— Missing or invalid authentication.404— No customer here has thisid. The customer may already be deleted. not-found422—display_nameis over 200 characters, orlow_balance_threshold_mcis negative. The response names the field. validation-error429— Rate limit exceeded.500— Unexpected server error.
See also
Soft-delete a customer by QuotaStack id.
/v1/customers/{id} | Idempotency-Key | Not used. This is a read. |
|---|---|
| Environment | Sandbox and Live |
| Auth | Tenant API key |
| Writes | Sets 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.
| Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | Your own |
Errors
401— Missing or invalid authentication.404— No customer here has thisid. The customer may already be deleted. not-found429— Rate limit exceeded.500— Unexpected server error.
Retrieve a customer by your external_id.
/v1/customer-by-external-id/{external_id} | 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. |
The external_id twin of getCustomer. Only your own external_id matches here, never a QuotaStack UUID.
| Field | Type | Meaning |
|---|---|---|
external_idrequired | string | 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. |
{
"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"
} | Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | QuotaStack-assigned customer UUID. |
tenant_idrequired | string (uuid) | |
environmentrequired | string |
Which environment this resource lives in. Determined by the API key prefix used ( |
external_idrequired | string | Your own ID for this customer, set when you created it. |
display_nameoptional | string | A label people see in the dashboard. |
overage_policyoptional | string | The value stored for this customer. |
low_balance_threshold_mcoptional | integer (int64)0–∞ | The Unit: millicredits, where 1 credit is 1000 millicredits. |
metadatarequired | object | Your own key-value tags, unchanged since the last write. |
created_atrequired | string (date-time) | |
updated_atrequired | string (date-time) |
Errors
401— Missing or invalid authentication.404— No customer here has thisexternal_id. The customer may already be deleted. not-found429— Rate limit exceeded.500— Unexpected server error.
See also
Update a customer by your external_id.
/v1/customer-by-external-id/{external_id} | Idempotency-Key | Required. Returns 422 without it. |
|---|---|
| Environment | Sandbox and Live |
| Auth | Tenant API key |
| Writes | Replaces 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.
| Field | Type | Meaning |
|---|---|---|
Idempotency-Keyrequired | string | 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 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 Derive the key from the business event rather than a random value, so a retry from anywhere reuses it — for example |
| Field | Type | Meaning |
|---|---|---|
external_idrequired | string | 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. |
| Field | Type | Meaning |
|---|---|---|
display_nameoptional | string | 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_policyoptional | string | 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. |
low_balance_threshold_mcoptional | integer (int64)0–∞ | Turns on the Unit: millicredits, where 1 credit is 1000 millicredits. If you leave it out: The old value stays. QuotaStack changes nothing. |
metadataoptional | object | 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. |
{
"display_name": "Ada Lovelace"
} {
"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"
} | Field | Type | Meaning |
|---|---|---|
idrequired | string (uuid) | QuotaStack-assigned customer UUID. |
tenant_idrequired | string (uuid) | |
environmentrequired | string |
Which environment this resource lives in. Determined by the API key prefix used ( |
external_idrequired | string | Your own ID for this customer, set when you created it. |
display_nameoptional | string | A label people see in the dashboard. |
overage_policyoptional | string | The value stored for this customer. |
low_balance_threshold_mcoptional | integer (int64)0–∞ | The Unit: millicredits, where 1 credit is 1000 millicredits. |
metadatarequired | object | Your own key-value tags, unchanged since the last write. |
created_atrequired | string (date-time) | |
updated_atrequired | string (date-time) |
Errors
400— QuotaStack could not parse the request body as JSON. bad-request401— Missing or invalid authentication.404— No customer here has thisexternal_id. The customer may already be deleted. not-found422—display_nameis over 200 characters, orlow_balance_threshold_mcis negative. The response names the field. validation-error429— Rate limit exceeded.500— Unexpected server error.
See also
Soft-delete a customer by your external_id.
/v1/customer-by-external-id/{external_id} | Idempotency-Key | Not used. This is a read. |
|---|---|
| Environment | Sandbox and Live |
| Auth | Tenant API key |
| Writes | Sets 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.
| Field | Type | Meaning |
|---|---|---|
external_idrequired | string | 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
401— Missing or invalid authentication.404— No customer here has thisexternal_id. The customer may already be deleted. not-found429— Rate limit exceeded.500— Unexpected server error.
Loading…