Ominvo beta launches July 23, 2026 — only 10 spots remain Join the waitlist →
All posts
Building OminvoDay 54

Day 54: Built the block system before we had anyone to block.

June 19, 20264 min read

Built fraud-and-abuse infrastructure today. Risk classification, a Block tab in admin, Stripe-cancel-on-block with no refund, audit-logged unblocks, and a TOS amendment so all of it holds up legally.

Zero paying customers. Zero blocked accounts. Built it anyway.

Why now and not later

The honest case for waiting — you build risk classifiers against real customer behavior, not theory. Better signals, fewer false positives, no work wasted on edge cases that never happen. Sam pushed back on building today for exactly that reason.

The honest case for building now — the first time someone files a chargeback, you don't want to be writing the cancel-with-no-refund flow at 2am. You want it sitting there, tested, with a TOS clause behind it. Same goes for the first abusive support thread, the first multi-account scam, the first spammy testimonial.

I overrode Sam. Documenting the disagreement here so future me can check whether the call was right.

What shipped

Five new tables in Supabase — risk_signals, disputes, blocked_businesses, appeals, plus a risk_level column on businesses with a traffic-light constraint (green, yellow, red). Everything RLS-locked. The block list reads through a security-definer policy so middleware can check block status with the anon client without infinite recursion.

A new Blocked tab in admin sits between Testimonials and Appeals. Two sections — pick a business and block them with a 5-500 character reason (seven canned reason templates, each drafted as a full notice referencing the relevant TOS section, around 70-90 words), and a Currently Blocked table showing who's blocked, why, when, and by whom. Each row has an Unblock button.

The block flow itself — cancel the Stripe subscription immediately with no proration and no refund, insert the block row, log a tos_violation signal, flip risk to red, set subscription status to canceled. The unblock flow does the inverse minus the Stripe restore. Cancelled subs can't be uncancelled via API. The customer has to resubscribe. That's a feature, not a bug — getting unblocked is supposed to be a clean break.

Stripe charge.dispute.created webhook now auto-flags chargebacks. Inserts a disputes row, logs a chargeback risk signal at high severity, sets the business risk_level to red. No automatic block — chargebacks happen for legitimate reasons sometimes, and we want a human eye on that decision.

Flagged testimonials write to risk_signals too. The obscenity filter from Day 50 was already auto-flagging — now those flags accrue as audit-trail signals, so a business that repeatedly tries to submit garbage testimonials shows up in the risk log even if we never manually intervene.

The TOS amendment

Section 14 of Terms of Service used to be three sentences. It's now a proper termination-for-cause clause covering chargebacks, spam, abuse, billing evasion, and Acceptable Use violations. No refunds on terminated accounts. 30-day appeal window. Retained records for legal compliance even after data deletion.

You can't enforce a block-without-refund unless your TOS authorizes it. Otherwise Stripe sides with the customer on every dispute, you eat the chargeback fees, and you've gained nothing. Section 14 had to land today, not Day 55.

What's not built yet

The user-facing side. There's no /blocked page for a redirected user to land on, no /appeal/[token] form for them to plead their case, no block-notice email going out. Day 55 ships all of that as a connected unit — middleware redirect, public-facing block page, appeal flow end-to-end, three Resend templates. Together so we're never in a state where the admin can block but the user has no recourse path.

Until then, the Block button is internal-only and produces no customer-visible artifact. If you're a beta tester reading this — relax, nobody can accidentally lock you out tonight.

See the full diff in the changelog, and yesterday's work in Day 53.

Tagged

#building#admin#trust-and-safety

Written by

The founder of Ominvo

Building review management for single-location small businesses. Join the waitlist →