Documentation

Revenue attribution

Connect Stripe or Polar and see which pages, channels and campaigns actually earn.

What revenue attribution does

Connect Stripe or Polar and your payments, refunds and fees flow into a Revenue tab, joined to the visits that produced them where a signal exists. You get two things: totals you can trust (net revenue with its parts shown) and, when you opt in, the journey behind each payment, which page, channel and campaign actually earned it. Each site connects its own account, from its Settings → Integrations screen, and the screen walks you through the provider you pick.

Connect Stripe

Open your site's integrations

DashboardSiteSettingsIntegrations

Each site connects its own Stripe account, so open the site you want to measure and go to its Settings.

Connect with a restricted, read-only key

Create a restricted key in Stripe with read access to charges, payment intents, checkout sessions, refunds and disputes, and paste it in. A couple of minutes, no code changes. From then on the Revenue tab fills.

Choose the snapshot payload style

If Stripe asks which payload style to send, choose Snapshot. The adapter consumes classic snapshot events; thin (v2) payloads are not read.

See the totals arrive

Net revenue and its parts appear in the Revenue tab. This much needs nothing from your visitors and no further setup. To tie a payment back to the visit that produced it, turn on attribution next.

Connect Polar

Create an Organization Access Token

PolarSettingsDevelopersNew Token

Scope it to the organization whose revenue we should read, with exactly two permissions: Orders: Read and Refunds: Read. Anything else should stay off; we never write to Polar. Copy the token (polar_oat_…, shown once) and paste it into the site's Integrations screen. The connect probe reads Orders, so a token missing that scope is refused on the spot rather than half-working later.

Create the webhook endpoint

PolarSettingsWebhooksAdd endpoint

Connecting hands you back a webhook URL for this site. Create an endpoint at that address in Polar and select the order, refund, checkout and subscription events, or simply all of them: anything we do not consume is acknowledged and ignored, never an error.

Choose the Raw format

Polar offers Discord and Slack formats on the same form. Those send a chat message instead of the event payload, so an endpoint that looks configured delivers nothing we can read. Choose Raw.

Paste the signing secret back

Reveal the endpoint's signing secret (whsec_…) and paste it into the Integrations screen. Totals already flow from the token alone; the webhook is the low-latency path that lands a payment within seconds instead of on the next sweep.

  • Revenue is read pre-tax.Polar is the merchant of record: it collects tax and remits it, so an order's tax-inclusive total includes money you never receive. We record the post-discount, pre-tax figure, the same number Polar's own dashboard reports, so a comparison against a tax-inclusive total will read lower, correctly.
  • There are no dispute events. Polar emits none as the merchant of record, so chargebacks stay empty for a Polar connection. That is the provider having nothing to report, not a gap in your data.
  • Sandbox is a separate host.A sandbox token does not work against production and vice versa, so objects created in Polar's sandbox will not appear here.

Turn on attribution (opt-in, and it asks something of you)

Revenue totals need nothing from your visitors. Tying a payment back to the visit that produced it is different on both counts: you turn Attributed revenue on when connecting your provider, and the linking itself carries a consent obligation you meet as the controller of your site.

The switch decides whether the link happens at all, not just what the script sends. An order_id that reaches us while it is off, from a cached copy of the script or a request you wrote yourself, is dropped before the event is stored; and the matcher skips your site entirely, so a payment is not linked through your own payments integration either. Conversions are still counted and revenue totals are unaffected; what stops is the journey.

You wire the consent, because only you can see it

We do not hold the hint back on a consent state we cannot see, so wiring this is yours to do. The best place to ask is your own checkout page: consent collected inside Stripe Checkout is consent for taking payment, and the analytics link is a different purpose with a different controller, you.

wherever you record the visitor's choice
oa.consent("granted"); // or "denied", which stops collection entirely
Switching attribution off leaves the journeys you already have; it stops new ones. Switching it back on picks up roughly the last month of payments on the next pass, that is how far back the matcher re-reads, and older ones stay as they are.

What the reports show

  • Net revenue with its parts printed: gross, refunds, disputes, fees. A flat month of quiet trading and a flat month of heavy refunds look different here, on purpose.
  • Gross and net share one chart; the gap between the lines is the cost of doing business.
  • Payments the join could not attribute still count in every total; they simply have no journey. There is no IP matching and no time-proximity guessing, by policy.
  • Currencies that could not be converted are listed beside the totals rather than silently dropped.

Disconnecting

Disconnect the credential

SettingsIntegrations

Disconnecting stops the flow immediately: no new payments, refunds or disputes are read from that account.

What stays and what stops

The revenue you already collected stays in your history. Only new inflow stops. Reconnecting later picks up roughly the last month on the first pass, the same window attribution re-reads.

Privacy

Card numbers never touch our servers, provider payloads are never persisted, and the customer reference is stored only as a non-joinable pseudonym. Revenue aggregates are readable over the API by the site owner; individual transactions never leave the dashboard.

Common questions