Legal
Security
Effective June 15, 2026
We take security seriously. Below is what we do; if you have questions or want to report an issue, email support@trybackstop.com. Everything here is a property of how the product is built — not a policy we promise to follow.
Defense in depth — isolation by several controls, not one
A fair question is whether row-level security is “enough.” It's an excellent floor — but it isn't the only layer, and that's the point. Your workspace data is separated by several independent controls, so a bug in any single one isn't a breach on its own:
- Row-Level Security in the database. Every workspace-scoped table has Postgres RLS enabled — and the tenant-root tables additionally forceit — so a logged-in operator's queries physically cannot return another workspace's rows. Postgres enforces it even if our application code has a bug.
- Cryptographically validated sessions.Every request re-validates your session's JWT signature against the auth provider's keys — we never trust an unverified cookie — and the edge gateway turns away unauthenticated traffic before it reaches a page.
- Server-side role re-checks.On top of RLS, every dashboard and admin action re-checks your workspace role on the server. RLS says “these rows”; the app says “and you're allowed to do this to them.”
- An isolated, server-only privileged client. The one database client that can bypass RLS — used only by webhook handlers and scheduled jobs — is server-only, can never reach the browser, and re-scopes every query by workspace itself.
- Then encryption, signed webhooks, fail-closed rate limits, and a tamper-resistant audit log on top (all below).
So — is RLS “the best”? It's the strong floor. What makes the whole thing trustworthy is the layering around it.
Least privilege & one-click revocation
- We ask for the narrowest Stripe access an action needs.Recovery — retrying failed invoices, applying your save offers, sending hosted card-update links — needs Stripe's
read_writescope. That grant is only ever used to recover your revenue into your Stripe account. Backstop never uses it to move money to itself. - Revoking us is a hard stop. You can revoke Backstop's access from your Stripe dashboard in one click, at any time. We treat a revoked connection as a kill switch — in-flight recovery is abandoned and the connection is flagged disconnected, so nothing keeps running against an account that has cut us off. There is no setting on our side that keeps recovery alive after you've revoked access.
What Backstop can and cannot do on your Stripe account
Stripe's OAuth grant is coarse by design (read vs read-write — there is no “recovery-only” scope), so we hold ourselves to a deliberately narrow set of actions and publish exactly what they are. For the strongest guarantee, connect with a scoped restricted key(see below) — then the “cannot” list is enforced by Stripe itself, not just by our code.
What Backstop does — and nothing more:
- Retry a payment on an invoice that already failed — charging the card on file for an invoice you issued. This only ever moves money into your account.
- Apply the save offers you configured (a coupon, pause, or plan change) when a customer accepts one.
- Pause, resume, cancel, or switch a subscription when a customer requests it on your hosted cancel / portal page, or when you do it from the dashboard.
- Create a reactivation checkout that a former customer chooses to complete.
- Let a customer update their own card (a Stripe SetupIntent — we never see the card number).
What Backstop never does:
- No refunds. We never send money back out of your account.
- No payouts, transfers, or top-ups. We never move your balance anywhere.
- No application fees, and no money to ourselves. Backstop is not a payment facilitator and is never in your money flow — charges settle to your Stripe balance and pay out to your bank. We cannot pay ourselves.
- No arbitrary charges. We can only retry invoices that already exist and already failed — we cannot create a brand-new charge out of nothing.
Want that enforced by Stripe rather than trusted to our code? Connect with a restricted key scoped to Write on Subscriptions, Invoices, Coupons, Customers, Checkout Sessions, SetupIntents, and Webhook Endpoints, and No access on Refunds, Payouts, Balance, and Transfers. With those denied at the key, Backstop is technically incapable of moving money out of your account or to itself.
What we never see
- Your Stripe password and login. The recommended way to connect is Stripe Connect OAuth — you authorize Backstopon Stripe's consent screen and revoke us with one click; with OAuth we never receive a key at all. If you instead choose the optional restricted-API-key connection, that key is encrypted at rest (AES-256-GCM), never exposed to the browser, and revocable from your Stripe dashboard at any time.
- Card numbers, CVV, full PAN. Card collection happens inside Stripe-hosted iframes (PCI SAQ-A). We only ever store the brand and last-4 digits for the card-expiry warning feature.
- Customer login credentials.Your customers don't log into the Service — hosted card-update and cancel pages are gated by single-use tokens with tight expiry windows.
Authentication
- Operator accounts authenticate via email + password (with bcrypt-hashed passwords) or magic link. Sessions are HTTP-only secure cookies, and every request validates the session's JWT signatureagainst the auth provider's published keys — we never trust an unverified cookie.
- Hosted customer pages use random, single-use, expiring tokens (32 bytes of entropy, 21-day TTL for card-update, 24h for cancel).
- The embed endpoint supports HMAC-SHA256 signatures with per-workspace secrets and a ±5 minute replay window. Workspaces can require signed requests once they've migrated their integration.
Data isolation
- Row-Level Security on every workspace-scoped table. Operator sessions can only read or write rows that match their workspace membership. We audit RLS policies before every schema change.
- Server-side “secret” database access is restricted to webhook handlers and scheduled jobs that need to bypass RLS for cross-tenant work — never reachable from the browser.
- All DB writes that mutate operator preferences flow through validated server actions (zod schema + RLS), not the client.
Data in transit
- HTTPS everywhere, with HSTS (2-year max-age,
includeSubDomains). Clickjacking protection (X-Frame-Options: DENY+ aframe-ancestorspolicy) on every dashboard and admin page, plusnosniffand a strict referrer policy site-wide. - Webhooks (Stripe + Resend) are signature-verified before any work happens.
- Outgoing emails go through SPF + DKIM aligned domains; Pro workspaces can verify their own subdomain so emails ship from
noreply@theirdomain.
Data at rest
- Postgres encrypted at rest (Supabase-managed AES-256).
- Tenant Stripe credentials get a second layer in our application. Restricted API keys and auto-provisioned webhook signing secrets are sealed with AES-256-GCM using a key derived from a master secret via HKDF-SHA256 — on top of the managed disk encryption — and their database columns are revoked from normal access, so only server-side code can ever decrypt them. A tampered ciphertext fails closed rather than returning garbage.
- Backups encrypted, retained per Supabase's policy. Point-in-time recovery available for disaster scenarios.
- Workspace embed-signing secrets generated with
pgcryptoper row.
Operational practices
- All deploys go through CI with a build, lint, type-check, and test step. Database schema changes require a migration file in source control.
- Webhook event payloads persist with their Stripe event ID as the primary key, so replays are idempotent and auditable.
- Inngest steps are durable — if a worker crashes mid-flow, the job resumes where it left off without re-sending an email or re-charging a customer.
- Production secrets are stored in our hosting provider's secret manager, never in git.
.env.localis git-ignored and only used for development. - Public endpoints are rate-limited with an atomic limiter that fails closed— if the database can't record a request, it's denied rather than allowed through.
- Crash reports sent to our error monitor are scrubbed of emails, card data, tokens, and secrets before they leave the server.
Audit log
- Sensitive actions — team and role changes, Stripe connect/disconnect, API-token and outbound-webhook changes, data exports, retry-policy and plan changes, and every AI-assistant write — are recorded to an audit log.
- Audit rows are written server-side with the acting user captured at write time, so someone can't edit or erase their own trail.
AI assistant
- The in-app assistant is read-only by default. With your explicit, per-action approval it can also apply a small set of low-risk workspace settings — display currency, email branding, retry schedule, offer policies, notification preferences, and reactivation settings — which it surfaces as a confirmation card showing the exact before/after change. Nothing is written until you click Approve, and it never acts on its own.
- Even when it acts, it runs the same permission-checked path a human button does: it re-authenticates as you, re-checks your plan and role, and re-validates and clamps every value server-side. It can never move or refund money, enter or rotate credentials or signing secrets, change team roles or access, delete data, or publish a cancel flow — those stay permanently outside what it can touch, with or without approval.
- It sees only an aggregate, redacted view of your workspace — recovery rates, MRR, decline mix, setup state — fetched under your own row-level-security scope. It is never given end-customer personal data, individual customer records, Stripe identifiers, API keys, or signing secrets.
- It's powered by xAI's Grok over their API with model-training / data-sharing disabled, so prompts aren't used to train models. The public marketing-site bot is anonymous, rate-limited, and answers only from our docs — it has no account access whatsoever.
Compliance
Zrionix Technology, Inc.is incorporated in the United States. We're a small team — we do not currently hold a SOC 2 Type II or ISO 27001 certification. SOC 2 Type II is on the roadmap.
Reporting a vulnerability
We welcome responsible disclosure. Email support@trybackstop.comwith reproducer steps and a reasonable disclosure window. We don't currently run a paid bounty but we'll publicly thank reporters who follow responsible-disclosure practice.