Revenue Watch
Reconcile paid Stripe Checkout sessions against the Supabase side effects that fulfill them.
Guides
Revenue Watch
Stripe says the customer paid. Your database says they are still on the free plan. That gap — a paid Checkout session with no matching side effect — is the most expensive kind of silent failure. Revenue Ledger Watch exists to catch it before support tickets arrive.
Revenue Watch reconciles paid Stripe Checkout sessions against Supabase rows you configure as fulfillment proof. Drift means your webhook handler failed, ran late, or wrote to the wrong place.
What it reconciles
| Source | What it represents |
|---|---|
| Stripe Checkout sessions | Payments Stripe believes completed in the configured window |
| Supabase side effects | Rows your app wrote in response — subscription, entitlement, credit grant, etc. |
PreFlight examines up to 50 paid sessions per run within a default 24-hour window. Each session is matched by the trace column you map on the Stripe integration.
Drift types
| Kind | Meaning |
|---|---|
missing_side_effect | Paid session with no matching Supabase row |
duplicate_side_effect | Multiple rows for the same payment trace |
untraceable_payment | Payment exists but trace ID cannot be resolved |
status_mismatch | Row exists but status column does not match expected value |
Requirements
Connect Stripe and Supabase on the same project at
/dashboard/providers.Configure shadow mapping on the Stripe integration:
priceId,verifyTableName, andverifyTraceColumntell PreFlight where fulfillment rows land.Run a snapshot manually from Revenue Watch or wait for the monitoring cron to schedule one.
Why it stays disabled until mapped
Revenue Watch cannot guess your data model. Until you map the table and trace column that represent fulfillment, the feature returns a warning — there is nothing deterministic to reconcile against.
Optional verification fields on the Stripe integration refine matching:
| Field | Purpose |
|---|---|
verifyStatusColumn | Column that must match verifyExpectedStatus |
verifyCreatedAtColumn | Timestamp column for recency checks |
verifyWindowSeconds | How long to wait for async side effects during shadow runs |
PreFlight can scan the connected Supabase OpenAPI schema and suggest the strongest mapping.
Reading a snapshot
Each Revenue Ledger Watch run produces an immutable snapshot:
| Field | Meaning |
|---|---|
| Checked sessions | Paid Stripe sessions examined in the window |
| Drift count | Sessions with no matching or mismatched side effect |
| Status | success (no drift), warning, or failed by severity |
| Affected table | The Supabase table used for reconciliation |
| Open drift | Specific session IDs that need investigation |
Snapshots persist in the Flight Recorder. Old snapshots are history — run a fresh snapshot after fixing drift to confirm it cleared.
Investigating drift
Confirm payment in Stripe. Open the flagged session ID in Stripe Dashboard and verify it reached
paid.Check webhook logs. Did
checkout.session.completedarrive? Did your handler return 2xx?Verify the mapping. Confirm
verifyTableNameandverifyTraceColumnmatch where your handler actually writes.Backfill and re-run. Re-process the missed event or insert the side effect, then trigger a fresh Revenue Watch snapshot.
Common root causes include webhook signature mismatch (STRIPE_WEBHOOK_PROBE_FAILED), wrong webhook URL, or writing the trace ID to a different column than mapped.
Scheduled reconciliation
The monitoring cron runs due Revenue Ledger Watch snapshots on a configured cadence (REVENUE_LEDGER_WATCH_CADENCE_MS). When scheduled reconciliation detects blocking drift:
- Alerts route through configured Slack, Discord, email, or on-call channels.
- Deploy Gates with
require_revenue_watch: truebegin blocking until drift clears.
Gate on revenue
A Deploy Gate can require a clean Revenue Watch snapshot before promoting. That prevents shipping billing or fulfillment changes while reconciliation is already broken — see Deploy Gates.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Feature shows warning, zero drift | Stripe or Supabase not connected, or mapping missing | Connect both providers and set verify fields |
| Every session shows drift | Wrong table or trace column | Re-check mapping against your webhook handler |
| Drift count won't clear | Side effect not backfilled | Insert rows for flagged session IDs, then run a new snapshot |
| Shadow passes, Revenue Watch fails | Different trace columns or windows | Align shadow mapping with Revenue Watch mapping |
