Mappings
Match Stripe customers to your rows, Stripe prices to your plans, and point at the columns that grant access.
Mapping tells ParityRail how your database relates to Stripe, so it knows exactly what to compare.
It reads your schema and pre-fills suggestions as soon as your database connects — so mapping is mostly reviewing, not typing. Three wizard steps, in order: who a customer is, what plan they’re on, and which columns say they’ve paid.
Before you start
- An access-state source connected on step 3 — Postgres with
USAGE+SELECTgrants so ParityRail can introspect your schema, or Clerk with readable metadata. - Stripe connected on step 2 — plan mapping pulls price suggestions from prices seen on your Stripe customers (or the demo fixtures).
- Admin role on the ParityRail project — every save-mapping action requires it.
- Know how your app already links to Stripe — a
stripe_customer_idcolumn, an email match, or a metadata key — so you can confirm the mapping ParityRail suggests instead of guessing.
Set up your mappings
- Step 4 — Identity mapping.ParityRail introspects your schema and auto-suggests a mapping — look for the “Suggested from your schema” chip. Confirm the Billing field (Stripe) dropdown (defaults to
stripe.customer.id), the App table, and the App column. For example:stripe.customer.id → users.stripe_customer_idMatching on a metadata key instead? Pickstripe.customer.metadata.<key>and fill in Metadata key:stripe.customer.metadata.user_id → users.idBill organizations too? Expand “Advanced: map organizations too (B2B billing)” and repeat the same mapping for your org table. Click “Save and continue”. - Step 5 — Plan mapping. Click “Load from Stripe” to prefill rows from prices seen on your customers. Each row is
Stripe price id/Lookup key(optional) /App plan value— for example:Stripe price id price_123 Lookup key (optional) pro_monthly App plan value proEdit the app plan value on any row, add more with “Add row”, then click “Save and continue” (needs at least one valid row). - Step 6 — Access mapping. Confirm the auto-suggested User table, User id column, and Email column, plus at least one of Plan column or Premium flag column. Set Free plan values chips:
free <blank — press Enter with nothing typed>The blank chip also matches customers with no plan value set. Optionally expand “Organizations (team/B2B billing)” for seat checks, or “Entitlement table (feature flags per user)” for per-feature drift. Click “Save and continue”.
Choosing an identity method
Identity mapping answers the first question: which row in your database is this Stripe customer? Pick whichever method already matches how you link the two.
Method A — Stripe customer id → a column (recommended default)
Map stripe.customer.id to a column like users.stripe_customer_id. The id is stable and unique, so this is the option to reach for first. Use it whenever you already store the Stripe customer id on a row.
Method B — Stripe customer email → your email column
Map stripe.customer.email to users.email. Use this only if you don’t store the Stripe customer id anywhere. Email is less reliable — customers change it, and it’s not guaranteed unique in your table.
Method C — Stripe metadata key → a column
Map a metadata field — for example stripe.customer.metadata.user_id or metadata.org_id— to a matching column. Use this when you stamp your own id into Stripe metadata at checkout. It’s also the usual way to map organizations for B2B billing.
Access mapping fields
ParityRail matches the Stripe price id first when resolving a plan, and falls back to the lookup key when there’s no match. Access mapping’s fields:
| Field | Purpose |
|---|---|
userTable | The table holding your app users. |
userIdColumn | Primary key of the user row (defaults to id). |
emailColumn | Email column (defaults to email). |
stripeCustomerIdColumn | The column storing the Stripe customer id, if you have one (optional). |
planColumn | The column that stores the plan value (optional). |
premiumFlagColumn | A boolean column marking premium access (optional). |
trialEndsAtColumn | When the trial ends, if you track it (optional). |
freePlanValues | List the plan values that mean no paid access — for example free. Add a blank entry to also match customers with no plan value set. |
Organizations (team/B2B billing)
Add an organization mapping for team and B2B billing. ParityRail uses it for seat checks — comparing billed seats against active members. It needs:
- Your org table and its id column.
- Optional Stripe customer id and plan columns.
- A members table with org-id, status, and active-status values.
Entitlement table (feature flags per user)
An entitlement is a per-feature on/off flag — for example, whether a customer can see advanced reports. Add an entitlement mapping to catch drift on these, feature by feature. It needs a table with subject-id, feature-key, and active columns.
Verify it worked
Each step gates its own “Save and continue” and confirms with a toast: “Identity mapping saved”, “N plan mapping(s) saved”, “Access mapping saved” (Clerk sources: “Clerk mapping saved”).
- A valid identity mapping shows a live mono preview line, for example
stripe.customer.id → users.stripe_customer_id. - Auto-suggested fields carry a “Suggested from your schema” (or “Suggested from Stripe”) badge — hover it to see why ParityRail picked that field.
- Populated table and column dropdowns are themselves a signal: if you can pick from a list instead of typing, introspection succeeded.
Troubleshooting
| Symptom | Fix |
|---|---|
No tables found — check the read-only role has USAGE + SELECT grants. | The read-only role connected but can’t see any tables — re-run GRANT USAGE ON SCHEMA and GRANT SELECT. On the demo database, run pnpm seed, then click “Retry introspection”. |
| Step stuck on “Introspecting your database schema…”, then an error | Click “Retry introspection”. Reconnecting the database on step 3 invalidates the cached schema, so re-enter the mapping step to re-introspect. |
No Stripe prices found on your customers yet — add rows manually. | Real-mode price suggestions come only from prices already seen on fetched customers. With none yet, type price ids and plan values by hand and Save. |
Pick at least a plan column or a premium flag column so ParityRail can tell paid access apart. | Access mapping needs plan OR premium mapped — map at least one to enable Continue. |
Pick the user id and email columns — this table has no “id”/“email” columns to preselect. | The chosen table lacks id/email columns. Select the columns that actually exist in that table from the dropdowns. |
| Suggestions look empty for some fields | Expected — ParityRail only ever suggests columns that exist. Anything uncertain is intentionally left blank for an explicit human pick. |