Connect Stripe and OakData turns your subscription data into revenue analytics — MRR, ARR, churn and ARPU on a dedicated Revenue dashboard — and links each paying customer back to the person who visited your site, so you can see who upgraded, to which plan, and their lifetime value, right on their profile in People.
The connection uses a read-only restricted API key. OakData can only read your customers, subscriptions and charges — it can never create charges, issue refunds, or change anything in your Stripe account. There are no webhooks for you to configure: OakData backfills on connect and refreshes whenever you open the Revenue dashboard.
1. Create a read-only key
Open Stripe's Create restricted key form — the name (OakData) and the read-only permissions are already filled in, so you just click Create key. It grants Read access to:
| Resource | Type | Description |
|---|---|---|
Customers | Read | Emails, names and metadata used to match people. |
Subscriptions | Read | Plans, status and amounts — the basis for MRR/ARR. |
Products | Read | Plan names shown on the dashboard and profiles. |
Charges & refunds | Read | One-off payments and refunds for lifetime value. |
Invoices | Read | Subscription billing history. |
Checkout Sessions | Read | Reads oak_distinct_id metadata for attribution. |
Payment Intents | Read | One-off payment attribution and status. |
Account | Read | The connected account's business name and currency. |
Copy the key — it starts with rk_live_ (or rk_test_ in test mode).
Read-only and revocable
A restricted key with only Read scopes can't move money or modify your account. To disconnect for good, delete the key on Stripe's API keys page — OakData stores it encrypted at rest and never exposes it again.
2. Paste it into OakData
Go to Project → Settings → Stripe revenue, paste the rk_… key, and click Connect Stripe. OakData validates the key, pulls in your customers, subscriptions and charges, and computes MRR. Open the Revenue tab to see it.
3. Attribute revenue to the right person
To link a Stripe customer to a tracked visitor, OakData resolves in this order:
| Method | Type | Description |
|---|---|---|
oak_distinct_id | metadata | Most precise, and works even for anonymous visitors. Put the current visitor id (oak.getDistinctId()) into the Stripe object's metadata at checkout. |
oak_user_id | metadata | Your own user id — the same value you pass to oak.identify(). Use this when checkout happens server-side away from the browser. |
email | fallback | If no metadata is present, OakData matches the Stripe customer email against the emails your visitors were identified with. |
Billing email ≠ login email
People often check out with a different email than the one they signed up with, so email matching alone misses them. Whenever you can, pass oak_distinct_id or oak_user_idin metadata — it's exact, and it captures anonymous upgrades that email never would.
The visitor id lives in the oak_did cookie (set by the tracking SDK) and is also available client-side as oak.getDistinctId(). Read it when you create the checkout and attach it as metadata:
import { cookies } from 'next/headers'
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(req: Request) {
const oakId = (await cookies()).get('oak_did')?.value
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: 'price_123', quantity: 1 }],
success_url: 'https://example.com/thanks',
// Copy the id onto the subscription (and customer) so OakData's
// sync sees it on the persisted objects, not just the session.
metadata: { oak_distinct_id: oakId ?? '' },
subscription_data: { metadata: { oak_distinct_id: oakId ?? '' } },
})
return Response.json({ url: session.url })
}Put metadata on the persistent object
OakData syncs Customers and Subscriptions, so attach the id to subscription_data.metadata or the Customer's metadata— not only the Checkout Session, which the sync doesn't page through. client_reference_id on Payment Links is copied onto the resulting subscription automatically.
Email fallback
Even without metadata, identified people are matched by email. Make sure you call oak.identify() with an emailtrait so there's something to match on:
oak.identify('user_123', { email: 'sam@example.com', name: 'Sam Rivera' })What you get
| Where | Type | Description |
|---|---|---|
Revenue dashboard | tab | MRR, ARR, active subscribers, ARPU, an MRR trend, revenue by plan, and your top customers. |
People list | pill | A plan + MRR pill on each paying person, plus a “Paying customers” filter. |
Person profile | panel | Plan, MRR, lifetime value and subscription status on the visitor you already know. |
FAQ
| Question | Type | Description |
|---|---|---|
How fresh is the data? | — | OakData backfills immediately on connect and refreshes each time you open the Revenue dashboard. Use “Re-sync” in Settings to force a refresh on demand. |
How is MRR calculated? | — | Active and past-due subscriptions, normalized to a monthly amount (annual ÷12, quarterly ÷3, weekly ×4.33). ARR is MRR ×12. |
Test mode? | — | Yes — connect an rk_test_ key to preview with your Stripe test data before going live. |