# Revenue & Stripe > Connect Stripe with a read-only key to see MRR, ARR and churn, and to know exactly which of your tracked people upgraded, to which plan, and their lifetime value. Source: https://oakdata.co/docs/integrations/stripe --- 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](https://oakdata.co/docs). 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](https://dashboard.stripe.com/apikeys/create?name=OakData&permissions[0]=rak_customer_read&permissions[1]=rak_subscription_read&permissions[2]=rak_product_read&permissions[3]=rak_charge_read&permissions[4]=rak_invoice_read&permissions[5]=rak_checkout_session_read&permissions[6]=rak_payment_intent_read&permissions[7]=rak_connected_account_read) form — the name (`OakData`) and the read-only permissions are already filled in, so you just click **Create key**. It grants Read access to: | Name | 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](https://dashboard.stripe.com/apikeys) — OakData stores it encrypted at rest and never exposes it again. ## 2. Paste it into OakData Open the **Revenue** tab in your project, paste the `rk_…` key, and click **Connect Stripe**. OakData validates the key, pulls in your customers, subscriptions and charges, and computes MRR. The preview behind the connect panel fills in with your real numbers as soon as the first sync lands. ## 3. Attribute revenue to the right person To link a Stripe customer to a tracked visitor, OakData resolves in this order: | Name | 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_id` in 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: **Checkout Session** ```ts 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 }) } ``` **Payment Link** ```ts // Pass the visitor id as client_reference_id on the Payment Link URL: // https://buy.stripe.com/xxx?client_reference_id={OAK_DISTINCT_ID} // then set it on the client from oak.getDistinctId(): const id = window.oak?.getDistinctId() const url = 'https://buy.stripe.com/xxx?client_reference_id=' + id ``` **Customer** ```ts // If you create the Customer yourself, stamp your user id on it — // OakData resolves it against the same id you pass to oak.identify(). await stripe.customers.update(customerId, { metadata: { oak_user_id: user.id }, }) ``` > **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 `email` trait so there's something to match on: **client** ```ts oak.identify('user_123', { email: 'sam@example.com', name: 'Sam Rivera' }) ``` ## What you get | Name | 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 | Name | 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. |