> ## Documentation Index
> Fetch the complete documentation index at: https://docs.topograph.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Billing Notifications

> Email and webhook alerts for low balance, high usage, and auto top-up events

Topograph can notify you about three billing events:

1. **Low balance**: your prepaid credit balance crosses a configured threshold.
2. **High usage**: your spend in a rolling window reaches a configured threshold. High usage has two independent passes, described below.
3. **Auto top-up**: an automatic recharge succeeds or fails.

Each notification can go out as an email to your account admins and as a webhook event delivered through the same webhook endpoint setup as company data events. You can independently enable or disable each channel per notification kind.

<Warning>
  All master toggles are **off by default on new accounts**. Nothing fires until an admin enables the specific kind they want from the Billing page. Channel sub-toggles (email / webhook) start at on, so flipping a master routes through both channels with a single click.
</Warning>

## How notifications fire

Notifications are evaluated after billable usage changes your balance or spend window. A notification is recorded once per threshold crossing, not once per request. The tier is rearmed when you refill or usage drops below the threshold and later crosses it again.

### Global vs per-workspace high usage

High usage runs two independent passes on every reserve. Both can fire in the same evaluation, and overriding one does not affect the other:

* **Global (account aggregate)**: sums billable usage across **every workspace** over the configured global period and checks it against your global tiers. Use this to alert on total account burn regardless of which workspace drove it.
* **Per-workspace**: sums billable usage for a single workspace over the configured workspace period and checks it against workspace tiers. Account-level workspace settings act as defaults; specific workspaces can override them.

Each pass has its own master toggle, channels, period, and tier list. High usage uses formal absolute thresholds. There is no multiplier or baseline comparison.

Auto top-up notifications fire when an automatic top-up succeeds or fails.

## Channels

Every notification kind has two channels, configured independently:

* **Email**: sent to account admins.
* **Webhook**: delivered through the same endpoints you already set up for `company.updated`.

You control each channel with a boolean. A notification is sent on a given channel when the kind's master switch and the channel switch are both on. Turning both channel switches off while leaving the master on keeps the event visible in recent notification history without dispatching email or webhook alerts.

## Webhook event types

All billing webhook payloads carry a `type` and `version: "1"`. Future schema changes will emit new event types rather than mutate v1.

### `billing.low_balance.triggered`

```json theme={null}
{
  "type": "billing.low_balance.triggered",
  "version": "1",
  "accountId": "acc_...",
  "tier": "warning",
  "balanceCents": 4523,
  "thresholdCents": 5000,
  "autoTopupEnabled": true,
  "firedAt": "2026-04-14T10:23:45.000Z"
}
```

The `tier` field is one of `warning`, `critical`, or `depleted`. You can configure multiple tiers via the REST API or dashboard.

### `billing.high_usage.triggered`

The high-usage event now carries a `scope` field that tells you which pass fired:

* `scope: "workspace"` with a set `workspaceId`: the per-workspace pass fired for that workspace.
* `scope: "global"` with `workspaceId: null`: the global aggregate pass fired for the whole account.

Per-workspace example:

```json theme={null}
{
  "type": "billing.high_usage.triggered",
  "version": "1",
  "accountId": "acc_...",
  "scope": "workspace",
  "workspaceId": "ws_...",
  "tier": "warning",
  "periodMinutes": 60,
  "periodSpendCents": 2300,
  "thresholdCents": 2000,
  "balanceCents": 87000,
  "firedAt": "2026-04-14T10:23:45.000Z"
}
```

Global aggregate example:

```json theme={null}
{
  "type": "billing.high_usage.triggered",
  "version": "1",
  "accountId": "acc_...",
  "scope": "global",
  "workspaceId": null,
  "tier": "critical",
  "periodMinutes": 60,
  "periodSpendCents": 61000,
  "thresholdCents": 50000,
  "balanceCents": 200000,
  "firedAt": "2026-04-14T10:23:45.000Z"
}
```

Each pass starts with a default warning tier. Add or edit tiers from the dashboard or via PATCH.

Workspace overrides only affect the per-workspace pass. Setting `highUsageEnabled: false` on a workspace override stops that workspace from firing its own per-workspace notifications but does not silence the global pass. If you want to silence the global pass too, turn off `globalHighUsageEnabled` at the account level.

### `billing.auto_topup.succeeded`

```json theme={null}
{
  "type": "billing.auto_topup.succeeded",
  "version": "1",
  "accountId": "acc_...",
  "amountCents": 10000,
  "previousBalanceCents": 350,
  "newBalanceCents": 10350,
  "thresholdCents": 5000,
  "paymentIntentId": "pi_...",
  "firedAt": "2026-04-14T10:23:45.000Z"
}
```

### `billing.auto_topup.failed`

```json theme={null}
{
  "type": "billing.auto_topup.failed",
  "version": "1",
  "accountId": "acc_...",
  "attemptedAmountCents": 10000,
  "currentBalanceCents": 350,
  "errorMessage": "card_declined",
  "paymentIntentId": null,
  "autoTopupDisabled": true,
  "firedAt": "2026-04-14T10:23:45.000Z"
}
```

When an auto top-up attempt fails, auto top-up is disabled to prevent retry loops. You receive this event once and then need to update your payment method to re-enable it.

## Configuration API

All endpoints are under `/v2/billing/notifications` and require your standard `x-api-key` header.

### Read the resolved config

```http theme={null}
GET /v2/billing/notifications/config
```

Returns the fully merged config: your stored overrides layered on top of the defaults. You always receive a complete shape, even if you have not configured anything yet.

A brand-new account that has never touched the config sees every master at `false` and every channel at `true`. Each kind ships with a single `warning` tier so flipping the master on immediately fires on one sensible threshold. Add more tiers (`critical`, `depleted`, extra bands) via PATCH if you want finer granularity.

```json theme={null}
{
  "lowBalanceEnabled": false,
  "lowBalanceEmailEnabled": true,
  "lowBalanceWebhookEnabled": true,
  "lowBalanceTiers": [
    { "tier": "warning", "cents": 100000 }
  ],
  "globalHighUsageEnabled": false,
  "globalHighUsageEmailEnabled": true,
  "globalHighUsageWebhookEnabled": true,
  "globalHighUsagePeriodMinutes": 1440,
  "globalHighUsageTiers": [
    { "tier": "warning", "cents": 100000 }
  ],
  "highUsageEnabled": false,
  "highUsageEmailEnabled": true,
  "highUsageWebhookEnabled": true,
  "highUsagePeriodMinutes": 1440,
  "highUsageTiers": [
    { "tier": "warning", "cents": 100000 }
  ],
  "autoTopupNotificationsEnabled": false,
  "autoTopupEmailEnabled": true,
  "autoTopupWebhookEnabled": true
}
```

### Update the config

```http theme={null}
PATCH /v2/billing/notifications/config
Content-Type: application/json

{
  "lowBalanceTiers": [
    { "tier": "warning", "cents": 10000 },
    { "tier": "critical", "cents": 2000 },
    { "tier": "depleted", "cents": 0 }
  ],
  "highUsagePeriodMinutes": 120,
  "highUsageTiers": [
    { "tier": "critical", "cents": 50000 },
    { "tier": "warning", "cents": 10000 }
  ],
  "lowBalanceEmailEnabled": false
}
```

All fields are optional. Any field you omit keeps its persisted value. You can send just `{"lowBalanceEmailEnabled": false}` to turn off low-balance emails without touching anything else.

### List recent notification events

```http theme={null}
GET /v2/billing/notifications/recent?limit=50
```

Returns the most recent billing notification events for your account, ordered newest first. Each row includes the kind, identifier, original payload, and delivery flags for email and webhook channels.

### Workspace overrides

If you use workspaces, you can override the high-usage rules for a specific workspace. Low-balance and auto-topup stay at the account level.

```http theme={null}
GET /v2/billing/notifications/workspaces/{workspaceId}/config
PATCH /v2/billing/notifications/workspaces/{workspaceId}/config
DELETE /v2/billing/notifications/workspaces/{workspaceId}/config
```

`GET` returns both the resolved account config and the raw workspace override row, so you can see which fields are overridden and which are inherited. `PATCH` upserts an override; any null field falls back to the account config at evaluation time. `DELETE` removes the override row (the workspace then inherits everything).

Example: give workspace `ws_batch` a much higher high-usage floor so nightly backfills don't page you, while keeping everything else at the account defaults:

```http theme={null}
PATCH /v2/billing/notifications/workspaces/ws_batch/config
Content-Type: application/json

{
  "highUsageTiers": [
    { "tier": "critical", "cents": 500000 },
    { "tier": "warning", "cents": 100000 }
  ]
}
```

## Verifying webhooks

Billing webhooks use the same webhook endpoint setup as your `company.updated` webhooks. The signature verification flow is identical:

Verify billing webhook signatures the same way you verify company data webhook signatures, then branch on the `type` field in the payload.

See the [Webhooks guide](/guides/webhooks) for the full verification walkthrough and retry semantics.

## Idempotency

Every billing notification is deduplicated at evaluation time. If the same threshold crossing is evaluated more than once, only one customer-visible notification is recorded.

High-usage notifications are deduplicated inside their rolling evaluation window, so two evaluations of the same threshold crossing do not produce duplicate customer alerts.

## Rearm semantics

Threshold notifications fire once when you cross a tier, then disarm. They rearm when the condition has clearly reversed:

* Low balance: when the balance recovers above the tier threshold.
* High usage: when the rolling spend falls below the tier threshold.

This means a single balance drop will not spam you with repeated `depleted` notifications, but a refill followed by another drop can send a fresh notification.

Auto-topup notifications are event-based, not threshold-based. Each attempt is evaluated independently.
