Overage
What happens when a customer spends past their balance — the three policies, the overage record, and how to reconcile arrears.
block), or pour it and write the difference on the tab (allow). The tab is a separate record — the card never goes negative. Quick take
- Three policies:
blockrefuses,allowpours and records,notifyis identical to allow today - A customer's own policy wins when set; the tenant default applies otherwise
- Balance never goes negative; the shortfall is written to its own overage record
- Amounts are millicredits, one record per over-spending call
- Read arrears with
GET /v1/customers/{id}/overage?from=&to=— both bounds required
Overage
Overage is what happens when a customer’s usage costs more than their balance holds. Your policy decides whether that call is refused, or allowed and written down as a debt. Balances never go negative. The debt lives in its own record.
The three policies
| Policy | What happens to the call | What is recorded |
|---|---|---|
block | Refused. Nothing is debited. | Nothing. |
allow | Allowed. The balance is spent down to zero. | The shortfall, as an overage record. |
notify | Allowed. The balance is spent down to zero. | The shortfall, as an overage record. |
notify behaves exactly like allow today. The engine treats the two words the same. There is no overage notification webhook yet. Pick notify if you want to record the intent, but do not wait for a message that is not sent.
Configuring the policy
Your tenant sets a default policy in the dashboard, under Organization settings. It applies when a customer has no value of its own.
A customer can set its own overage_policy field. When set, it wins over the tenant default. Send an empty string to clear it, and the tenant default applies again.
This works both ways. A tenant that allows overage can still block one risky customer. A tenant that blocks overage can still allow one trusted customer.
On the customer endpoint, a bad value now returns 422 and names the field. It used to return a database error.
What block does
block is all-or-nothing. If the call costs more than the balance holds, nothing is debited and the usage is refused. No partial spend. No overage record.
A refused call is a settled answer, not a failure to retry. QuotaStack does not keep retrying an unaffordable usage event, because a customer who cannot pay today may not be able to pay in an hour either, and a retry loop would block every event behind it.
What allow does
allow spends what is there and records the rest.
Say a customer has 3,000 millicredits and makes a call that costs 5,000:
- 3,000 millicredits are debited. The balance is now 0.
- The remaining 2,000 is written to the overage record.
- The balance is 0, not -2,000. Balances stop at zero.
You bill the 2,000 later, from the overage record.
The overage record
Every over-spending call writes one row. Each row holds:
- the billable metric that was used,
- the amount in millicredits, always greater than zero,
- the environment,
- the idempotency key of the call that caused it,
- when it happened.
The idempotency key matters. A retried call cannot be counted twice, because the record is unique on your tenant, the environment, and that key.
Reading arrears back
Ask for a customer’s overage over a window:
curl "https://api.quotastack.io/v1/customers/{customer_id}/overage?from=2026-07-01T00:00:00Z&to=2026-08-01T00:00:00Z" \
-H "X-API-Key: qs_live_..."
Four rules, all enforced:
fromandtoare both required. Without them the call returns422.- The window is half-open, written
[from, to). A row created exactly attois not included. tomust be afterfrom.- An unknown customer returns
404. Reading never creates a customer.
The response groups by metric and gives you a total:
{
"data": [
{ "billable_metric_key": "image_generation", "overage_mc": 2000 },
{ "billable_metric_key": "tokens", "overage_mc": 450 }
],
"total_overage_mc": 2450,
"period": { "from": "2026-07-01T00:00:00Z", "to": "2026-08-01T00:00:00Z" }
}
Amounts are millicredits. Rows are sorted by metric key.
Reconciling at the end of a period
For postpaid subscriptions, the subscription.renewed webhook carries a usage summary for the period that just closed. That is the push side.
The read endpoint above is the pull side. Use it when you want to reconcile without waiting for the webhook, or when you need the numbers again and do not want to replay a delivery. Both read the same records, so they agree.
The full billing recipe is in Bill overage in arrears.
Seeing it in the dashboard
The customer page has an Overage tab. Pick a window — the current billing period, the last 7 days, or the last 30 days — and it shows the per-metric totals and the sum.
The tab also shows which policy is in force: the customer’s own value if it has one, or the tenant default.
Common mistakes
Don't send null expecting to clear a customer's overage_policy override
null leaves the stored value unchanged. Send an empty string to clear it — the same rule display_name already follows.
Don't pick notify expecting a notification
It behaves exactly like allow. No overage webhook is emitted yet.
Don't call the overage endpoint without from and to
Both are required and the call returns 422 without them. The window is half-open — to is excluded.
Don't expect a negative balance to represent arrears
Balances stop at zero. Arrears live in the overage record, and that is what you bill from.
Loading…