← Back to list
01Issue
FeatureOpenSwamp ClubPublic
AssigneesNone
Relationships
#2232 Billing telemetry: surface scheduled cancellations (cancelAtPeriodEnd) and emit subscription_canceled reliably
Opened by keeb · 9/17/2026
Problem
A customer who cancels in Stripe (billing portal or dashboard) is invisible in telemetry until their period actually ends.
- Cancelling sets
cancel_at_period_end=trueand Stripe sendscustomer.subscription.updated. The gateway maps it tosubscription.synced(lib/infrastructure/stripe/stripe-billing-gateway.ts:430), androutes/api/billing/webhook.ts:35-51emitssubscription_syncedwithstatus: "active". The properties (collectiveId, plan, interval, seats, status, username) omitcancelAtPeriodEnd, so the cancel is indistinguishable from a routine renewal sync. The flag is persisted to Mongo (lib/app/billing/sync-subscription.ts:250) but never reaches the event lake. subscription_canceledonly fires oncustomer.subscription.deletedat period end (webhook.ts:66-77). Churn shows up the day it happens, never while it is still pending.subscription_canceledis sent with fire-and-forgettrack(...).catch(() => {}), whilesubscription_startedusesreliableTrack. A dropped emit loses the churn event with no retry; only Mongobilling_subscription.statusrecords it.
Observed: a paying Team customer opened the billing portal and cancelled on 2026-08-21. The only trace in ClickHouse is a billing_portal_opened followed 25s later by a subscription_synced with status: active. Nothing in the lake says the subscription won't renew.
Proposed solution
- Add
cancelAtPeriodEnd(andcurrentPeriodEnd) to thesubscription_syncedproperties inwebhook.ts. - Emit a distinct
subscription_cancel_scheduledevent when a synced snapshot flipscancelAtPeriodEndfalse→true, andsubscription_cancel_revertedon true→false. Compare against the stored doc beforeapplyStripeState. - Send
subscription_canceledwithreliableTrack, same assubscription_started. Churn is as revenue-critical as a new customer.
Alternatives
- Read
billing_subscription.cancelAtPeriodEndfrom Mongo directly. This works for one-off checks but keeps churn out of the lake, so the ClickHouse/Grafana revenue boards can't show pending churn.
02Bog Flow
Open
No activity in this phase yet.
03Sludge Pulse
Sign in to post a ripple.