---
title: "Webhook events"
description: "Every event QuotaStack sends, grouped by object, with when it fires and when it does not."
---

# Events

QuotaStack POSTs one of these events to your webhook URL when something happens. Every entry links to its payload shape, when it fires, and when it does not.

## credit.*

- [`credit.consumed`](/docs/api/events/credit.consumed.md) — Fired when credits are consumed (debit ledger entries committed).
- [`credit.exhausted`](/docs/api/events/credit.exhausted.md) — Fired once when a customer's effective balance crosses from > 0 to ≤ 0 (consumption, reservation create/commit, or expiry). Always enabled. Effective may be negative when reserved holds remain after balance drops; that still counts as exhausted. Re-arms when effective recovers above 0. A high→≤0 one-shot emits exhausted only (not also low_balance).
- [`credit.expired`](/docs/api/events/credit.expired.md) — Fired when a credit block's remaining balance expires (the block reached its expires_at). `data` is the expiry ledger entry; its amount is the millicredits written off. See `credit.expiring_soon` for the proactive lead-time warning.
- [`credit.expiring_soon`](/docs/api/events/credit.expiring_soon.md) — Fired once per credit block when that block's remaining balance will expire within the tenant's `credit_expiring_soon_hours` lead window (default 0 = off; recommended 72). Distinct from `credit.expired` (fires when the block actually expires) and from post-expiry `credit.exhausted` / `credit.low_balance`. Debounced per (customer, block_id); emission is best-effort.
- [`credit.granted`](/docs/api/events/credit.granted.md) — Fired when credits are granted to a customer (any source).
- [`credit.low_balance`](/docs/api/events/credit.low_balance.md) — Fired once when a customer's effective balance (balance − reserved) crosses below the configured low_balance threshold while remaining > 0. Re-arms on grant / reservation release when effective recovers to or above threshold. Default tenant threshold is 0 (off). Exhausted cliff (high → ≤0) does not emit this event.

## subscription.*

- [`subscription.canceled`](/docs/api/events/subscription.canceled.md) — Fired when POST /v1/subscriptions/{id}/cancel cancels a subscription — immediately or at period end (inspect `data.status` and `data.cancel_at_period_end`).
- [`subscription.contract_ended`](/docs/api/events/subscription.contract_ended.md) — Fired when a postpaid contract_end is reached.
- [`subscription.contract_ending_soon`](/docs/api/events/subscription.contract_ending_soon.md) — Fired N days before a postpaid contract_end (per subscription.contract_ending_soon_days).
- [`subscription.created`](/docs/api/events/subscription.created.md) — Fired when a subscription is created via POST /v1/subscriptions (including trials). Not fired for imported subscriptions — those emit subscription.imported instead.
- [`subscription.downgraded`](/docs/api/events/subscription.downgraded.md) — Fired when a downgrade is requested via POST /v1/subscriptions/{id}/downgrade. The variant change applies at the next renewal; `data` is the subscription with the pending change.
- [`subscription.expired`](/docs/api/events/subscription.expired.md) — Fired when an overdue subscription passes its grace period.
- [`subscription.imported`](/docs/api/events/subscription.imported.md) — Subscription imported via legacy cutover.
- [`subscription.paused`](/docs/api/events/subscription.paused.md) — Fired when POST /v1/subscriptions/{id}/pause pauses a subscription.
- [`subscription.renewal_due`](/docs/api/events/subscription.renewal_due.md) — Fired N days before a prepaid subscription's period_end (default 3).
- [`subscription.renewal_overdue`](/docs/api/events/subscription.renewal_overdue.md) — Fired when a prepaid subscription's period_end passed without renewal.
- [`subscription.renewed`](/docs/api/events/subscription.renewed.md) — Fired when a subscription advances into a new billing period — prepaid renewal, postpaid auto-advance, scheduled downgrade, or the tenant-called POST /renew. Unlike the other subscription events, `data` is NOT a Subscription object: it is a unified renewal summary carrying the just-ended period's usage/overage rollup.
- [`subscription.resumed`](/docs/api/events/subscription.resumed.md) — Fired when POST /v1/subscriptions/{id}/resume resumes a paused subscription.
- [`subscription.upgraded`](/docs/api/events/subscription.upgraded.md) — Fired when POST /v1/subscriptions/{id}/upgrade switches the plan variant (effective immediately). `data` is the post-upgrade subscription.

## customer.*

- [`customer.imported`](/docs/api/events/customer.imported.md) — Customer imported via legacy cutover.

## test.*

- [`test.ping`](/docs/api/events/test.ping.md) — Manually triggered test event (POST /v1/webhooks/test). Signed and delivered like every real event. `data.message` says what it is; `data.requested_at` is when the test was requested.

21 of 21 events documented in full.
