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 + SELECT grants 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_id column, an email match, or a metadata key — so you can confirm the mapping ParityRail suggests instead of guessing.

Set up your mappings

  1. 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_id
    Matching on a metadata key instead? Pick stripe.customer.metadata.<key> and fill in Metadata key:
    stripe.customer.metadata.user_id  →  users.id
    Bill organizations too? Expand “Advanced: map organizations too (B2B billing)” and repeat the same mapping for your org table. Click “Save and continue”.
  2. 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         pro
    Edit the app plan value on any row, add more with “Add row”, then click “Save and continue” (needs at least one valid row).
  3. 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”.
For Clerk sources, step 4 shows “Customer matching is automatic” — there’s nothing to map, just click Continue. Step 6 asks “Which Clerk fields say who has paid access?” and offers metadata-field pickers instead of SQL columns. See Connect Clerk.

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:

FieldPurpose
userTableThe table holding your app users.
userIdColumnPrimary key of the user row (defaults to id).
emailColumnEmail column (defaults to email).
stripeCustomerIdColumnThe column storing the Stripe customer id, if you have one (optional).
planColumnThe column that stores the plan value (optional).
premiumFlagColumnA boolean column marking premium access (optional).
trialEndsAtColumnWhen the trial ends, if you track it (optional).
freePlanValuesList 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

SymptomFix
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 errorClick “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 fieldsExpected — ParityRail only ever suggests columns that exist. Anything uncertain is intentionally left blank for an explicit human pick.