---
title: "2026-07-26 · Balance alert webhooks"
description: "Two new webhook events — credit.low_balance when spendable credits cross a threshold you set, and credit.exhausted when they hit zero. Both track effective balance and fire once per episode rather than on every debit."
order: 6
date: "2026-07-26"
---

# Balance alert webhooks

Additive release. Nothing existing changes behaviour — but you can now find out that a customer is running low *before* their next call fails, instead of inferring it from `credit.consumed` deltas.

## Added — `credit.low_balance`

Fires when a customer's **effective** balance crosses below a threshold you configure, while still remaining above zero. A soft warning: prompt a top-up, show a banner, send an email.

Off by default. The tenant threshold `low_balance_threshold_mc` starts at `0`, which disables the event.

```bash
# Tenant default (admin session — owner or admin role)
curl -X PATCH https://api.quotastack.io/v1/admin/tenants/{tenant_id}/config \
  -H "Cookie: qs_admin_session=..." \
  -H "Idempotency-Key: config-low-balance:{tenantId}" \
  -H "Content-Type: application/json" \
  -d '{"low_balance_threshold_mc": 5000}'
```

Individual customers can override it via `PATCH /v1/customers/{customer_id}` with your normal API key — `null` inherits the tenant default, `0` turns the event off for that customer alone.

## Added — `credit.exhausted`

Fires when effective balance crosses from above zero to zero or below. Always on, with no tenant toggle, because a zero balance is always worth knowing about.

Effective balance can go negative when a hold outlives the balance behind it — a block expiring while a reservation is still open, for instance. That still counts as exhausted.

## How they behave

| Behaviour | Detail |
|---|---|
| Balance tracked | **Effective** — `balance − reserved_balance − pending_balance`, so a large reservation can trip a threshold with no ledger debit |
| Firing | Once per episode, on the **crossing**. Further debits while still below do not re-fire |
| Re-arming | A grant, restore, or reservation release that lifts balance back over the line re-arms the event |
| Both at once | A single drop from above the threshold to zero emits `credit.exhausted` **only**, never both |
| Failure mode | Best-effort — emission never fails the underlying debit, reservation, or expiry |

Payloads carry `before_effective_balance`, `after_effective_balance`, `threshold_mc`, and `trigger`, on top of the standard envelope.

## Not included

These are webhooks only. QuotaStack does not send email and does not automatically top anyone up — the events are the signal, and what you do with them is yours.

Proactive warnings *before* a block's `expires_at` shipped separately as [`credit.expiring_soon`](/docs/changelog/2026-07-26-proactive-expiry-warnings). `credit.expired` continues to fire when a block actually expires, and if that drop zeroes the customer out you will get `credit.exhausted` alongside it.

See [`credit.low_balance`](/docs/api/events/credit.low_balance) and [`credit.exhausted`](/docs/api/events/credit.exhausted) for the full payloads.
