Topograph can notify you about three billing events: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.
- Low balance: your prepaid credit balance crosses a configured threshold.
- High usage: your spend in a rolling window reaches a configured threshold. High usage has two independent passes, described below.
- Auto top-up: an automatic recharge succeeds or fails.
How notifications fire
Notifications are evaluated after every billable reserve. The moment a request debits credits from your account, the billing layer checks whether any threshold was just crossed and records an audit row inbilling_notification_event. A row is only recorded once per crossing: if you drop from €100 to €40 and the warning threshold is €50, we fire one warning row, not one per request that ticked the balance down. The tier is rearmed when you refill above the threshold.
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 your billable events across every workspace over the last
globalHighUsagePeriodMinutesand checks it againstglobalHighUsageTiers. Fires once per account withworkspaceId: nullon the event row. Use this to alert on total account burn regardless of which workspace drove it. - Per-workspace: sums billable events for the triggering workspace over the last
highUsagePeriodMinutesand checks it againsthighUsageTiers. Fires once per workspace withworkspaceIdset on the event row. The values you set on the config act as the default for every workspace; specific workspaces can override them via the workspace config endpoint.
Channels
Every notification kind has two channels, configured independently:- Email: sent via Resend to the admins of your Clerk organization (with a fallback to any user on the account if Clerk is unavailable).
- Webhook: delivered through Svix using your existing account
svixApplicationId. Received through the same endpoints you already set up forcompany.updated.
billing_notification_event but nothing is dispatched.
Webhook event types
All billing webhook payloads carry atype and version: "1". Future schema changes will emit new event types rather than mutate v1.
billing.low_balance.triggered
tier field is one of warning, critical, or depleted. You can configure up to 10 tiers via the REST API. The starter default is a single warning tier at €1000, a buffer that comfortably covers the 5 to 7 business day SEPA and wire settlement window.
billing.high_usage.triggered
The high-usage event now carries a scope field that tells you which pass fired:
scope: "workspace"with a setworkspaceId: the per-workspace pass fired for that workspace.scope: "global"withworkspaceId: null: the global aggregate pass fired for the whole account.
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
billing.auto_topup.failed
Configuration API
All endpoints are under/v2/billing/notifications and require your standard x-api-key header.
Read the resolved config
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.
Update the config
{"lowBalanceEmailEnabled": false} to turn off low-balance emails without touching anything else.
List recent notification events
billing_notification_event rows for your account, ordered newest first. Each row includes the kind, identifier (tier name for threshold events, succeeded or failed for auto-topup), the original payload, and two flags: emailSent and webhookSent. The flags are flipped by the dispatch path after each channel successfully delivers.
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.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:
Verifying webhooks
Billing webhooks use the same Svix application as yourcompany.updated webhooks. The signature verification flow is identical:
Idempotency
Every billing notification is assigned a stablededupKey at evaluation time. If the same crossing is re-evaluated (for example, because a Temporal activity retried), the second attempt is caught by a unique constraint on dedupKey and no second audit row is written.
Dedup key shapes:
- Low balance:
${accountId}:low_balance:${tier} - Global high usage:
${accountId}:global:high_usage:${tier}:${periodBucketIso} - Per-workspace high usage:
${accountId}:${workspaceId}:high_usage:${tier}:${periodBucketIso} - Auto top-up:
${accountId}:auto_topup:{succeeded or failed}:${paymentIntentId or workflowRunId}
floor(now / periodMs) * periodMs), so two fires inside the same period collapse into one dedup key. The global pass uses globalHighUsagePeriodMinutes for its bucket; the per-workspace pass uses the effective highUsagePeriodMinutes (after any workspace override).
Rearm semantics
Threshold notifications follow a Schmitt-trigger pattern. A tier fires once when you cross it, then disarms. It rearms when the condition has clearly reversed:- Low balance: when the balance recovers strictly above the tier’s
centsvalue. - High usage: when the period spend falls strictly below the tier’s
centsvalue.
depleted notifications, but if you refill to €100 and then drain back to €0, you get a fresh notification.
Auto-topup notifications are event-based, not threshold-based. Each attempt is its own row; there is no state to rearm.