Quickstart
What ParityRail does, the eight-step setup, demo mode, and what read-only means.
What ParityRail does
Every payment makes a promise about what a customer can access. ParityRail checks that your app keeps that promise. When it doesn’t, it flags the gap as an access incident — a specific mismatch it found and can help you fix.
It runs one loop, called a fulfillment check — a read-only comparison of what billing promised against what your app grants. Here’s every stage:
| Stage | What happens |
|---|---|
| Promise | Every billing event defines exactly what access the customer is owed. |
| Check | A fulfillment check compares that promise against your live app state. |
| Incident | Each unfulfilled promise is filed as an access incident, with evidence. |
| Repair | A single-row, approval-gated fix with a before/after diff. |
| Verify | After the repair, the customer is re-checked until the promise holds. |
| Ledger | Every promise, check, incident, and repair lands in the Access Ledger. |
Before you start
- A ParityRail account with a project created — this walkthrough lands you on that project’s onboarding wizard.
- Admin role on the project. Every connect action (Stripe, database, Clerk) requires admin; a member can view and run the dry run but can’t connect integrations.
- One billing source (a Stripe account, or nothing — demo mode works) and one access-state source (a Postgres/Supabase database, or a Clerk instance whose metadata holds plan, premium, or trial).
- Nothing to install in your app to read your data. Only two steps touch your own systems: creating a read-only database role, and, optionally, registering a Stripe webhook.
Set up in eight steps
Onboarding lives at Project → Onboarding and walks you through every connection. Every step only reads — nothing changes until you turn on repairs.
- In the dashboard, open Project → Onboarding (URL
/projects/{projectId}/onboarding). The wizard is 8 numbered steps on a left rail: Stack → Stripe → Database → Identity mapping → Plan mapping → Access mapping → Dry run → Repair mode. It resumes where you left off, so you can leave and come back. - Step 1 (Stack): pick Billing (Stripe, pre-selected), Auth (Clerk / Supabase Auth / Other), and Database (Supabase / Postgres), then click Save and continue. Note: choosing Auth = Clerk here only tunes hints — it does not make Clerk your access source. You choose that with the tab on step 3.
- Step 2 (Stripe): either paste a restricted key and click Test & connect, or click Connect demo datasetin the dashed “Use demo mode” card to load 250 fixture customers with no external calls. See Connect Stripe.
- Step 3 (Database): connect Postgres with a read-only role, or switch to the Clerk tab. See Connect your database or Connect Clerk.
- Steps 4–6 (mappings):review ParityRail’s auto-suggested identity, plan, and access mappings and save each. See Mappings.
- Step 7 (Dry run): click Run read-only scan. Nothing is written.
- Step 8 (Repair mode): choose Detect only, Approval required, or Auto-repair safe to finish setup.
Copy-paste reference
The onboarding URL for any project follows this pattern:
/projects/<your-project-id>/onboardingIf you chose Stripe demo mode on step 2, the seeded demo app database — useful for step 3 — connects with:
postgres://demoapp:demoapp_dev@localhost:54322/demoappVerify it worked
Two signals tell you setup actually worked, and neither requires trusting that a step “looked” connected:
- Step 7 (Dry run): clicking Run read-only scanproduces a toast reading “Read-only scan complete — no changes were made” and four stat cards — Customers checked, Access incidents, Revenue at risk, and Changes made (always
0, hinted “Dry runs never write”). If it found incidents, a “By severity” breakdown and a “Review them in the incident inbox” link appear. The left rail shows a green check mark on every completed step. - Finishing step 8flips the onboarding page to a “Setup complete / Active” summary card, with a green status row for each of Stripe, App database, Identity mapping, Plan mappings, and Access mapping.
Troubleshooting
| Symptom | Fix |
|---|---|
| The wizard opens on a later step than you expected. | It resumes at your saved progress. Use the numbered left-rail buttons to jump back to any completed step — only steps you've already reached are clickable. |
| Continueis disabled, with a grey hint under it (for example, “Connect Stripe to continue”). | The step's requirement isn't met yet — finish the connect or mapping the hint names, then Continue enables itself. |
| Other connect buttons work for teammates, but not for you. | You likely have the member role. Connecting Stripe, a database, or Clerk requires admin — ask an owner or admin to elevate you. |
| You already finished setup and want to redo it. | Open the completed onboarding page and click Restart setup — it reopens the wizard from step 1. |
Demo mode
No Stripe account handy? On the Stripe step, choose Connect demo dataset. Demo mode loads a fixed dataset of 250 customers with realistic billing states and seeded access incidents, so you can see the full loop end to end. No external calls are made, and the demo Stripe integration never receives real webhook traffic.
What read-only means
ParityRail only ever reads. It can’t touch your billing or your live app data unless you explicitly turn on repairs.
- Stripe — a restricted, read-only API key. ParityRail never writes to Stripe.
- Postgres — a read-only role. Each connection also sets
default_transaction_read_only = onas a second safeguard, so it physically cannot write. - Clerk — read-only too, if you use it instead of a database. There, repairs are guided rather than automated.
Repairs are opt-in and isolated. They need a separate database role with narrow, column-scoped UPDATE grants.
- Every repair waits for your approval by default.
- You see a before/after diff before it runs.
- ParityRail re-verifies the customer afterward.
Leave the repair role unset to stay detect-only.
The incidents it catches
ParityRail detects eight classes of access incident between your billing and your app. Grace statuses like past_due and incomplete never trigger a revocation.
| Class | Incident | What it means |
|---|---|---|
M1 | Paid but no access | The customer pays for a plan but your app still shows them as free or locked out. |
M2 | Access but not paid | The subscription ended or never paid, but your app still grants premium access. |
M3 | Plan mismatch | Billing and your app disagree on which plan the customer is on. |
M4 | Trial mismatch | The trial end date in your app does not match the subscription's trial. |
M5 | Seat mismatch | Billed seat quantity differs from the count of active members. |
M6 | Mapping missing | A billing customer or app row could not be matched by the identity mapping. |
M7 | Webhook lag | A recent billing change has not been reflected within the tolerated window. |
M8 | Entitlement mismatch | A feature entitlement is missing or lingering relative to the billing promise. |