Polar integration

Polar analytics: MRR, churn and revenue by traffic source

BugOtter reads your Polar organization with a token that can only read, and shows what each app earns: MRR and how it moved, revenue after refunds, trials, and who is leaving and why. Pass one line of checkout metadata and it also shows which traffic sources brought the paying customers.

  • 4read scopes
  • Net of taxrevenue and MRR
  • 90 daysfirst sync

Three things to know

  • A token that only reads

    Four read scopes, nothing that can write. BugOtter never creates checkouts or refunds.
  • Why people cancelled

    Polar's cancellation reason sits next to each cancellation, with any errors that customer hit first.
  • Revenue by source

    One metadata field on the checkout covers the first payment and every renewal.

Connect Polar

An organization access token with four read scopes. Nothing that can write.

  1. Create an organization token

    In your organization's Settings (Settings → Access Tokens in Polar's docs). Name it “BugOtter”.
  2. Pick read scopes only

    products:read, orders:read, subscriptions:read and refunds:read.
  3. Paste it on Revenue

    BugOtter reads your organization's name and checks it can list products.
    Tokenpolar_oat_…
  4. Match products to apps

    BugOtter suggests a match from the product name. You confirm it once.
  • Pick an expiry you're comfortable with. refunds:read is there so refunds are dated correctly.
  • The token is stored encrypted, showing only its last 4 characters. Each Polar product belongs to one app.
  • If the token expires, the connection shows Needs a new key and the Revenue page says why.

90 days

The first sync reads 90 days of orders and every active, trialing and past-due subscription.

After that, every 15 minutes.

Token steps and scopes checked Nov 4, 2026 against Polar's organization access token docs.

What you'll see

Polar rows sit next to Stripe, Lemon Squeezy and Creem in the same numbers.

BugOtter's Revenue view: MRR of $1,389, a waterfall of how it moved in 30 days, a forecast, revenue, paying subscribers and trials
  1. MRR per app, net of the tax Polar collects.
  2. How it moved: new, upgrades, downgrades and cancellations.
  3. A forecast if the last 30 days repeat.
  4. Trials, and what they're worth if they pay.
  • Revenue after refunds. With refunds:read, a refund shows on the day Polar made it, even for an order older than 90 days.
  • Leaving soon: subscriptions set to cancel at the end of the period.
  • Why people cancelled. Polar asks customers for a reason when they cancel. BugOtter shows it next to the cancellation, and the churn evidence checks whether that customer hit an error or an outage in the 14 days before.
  • Customers, anonymous: status, price, lifetime paid and first-visit source. No names or emails.
  • Revenue per traffic source, once checkouts carry the visit id.

Link Polar payments to traffic sources

Polar copies checkout metadata to the order and the subscription, so one field covers the first payment and every renewal.

BugOtter traffic cards: channels with visitors, revenue and error rate, top pages, and countries with revenue
  1. Revenue per channel, next to its visitors.
  2. Errors per channel, so a source that breaks checkout stands out.
  3. Revenue per country, from the same visits.

First, read the visit id in the browser and send it with your checkout request:

Browser
// In the browser, before you start the checkout
const visitorId = window.bo?.visitorId?.(); // "bo_…" or undefined

await fetch("/api/checkout", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ productId, visitorId }),
});

Then put it in the checkout's metadata as bugotter_visitor_id:

Server: create the Polar checkout
// On your server: create the checkout with your app's own Polar token
const res = await fetch("https://api.polar.sh/v1/checkouts/", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POLAR_ACCESS_TOKEN}`, // needs checkouts:write
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    products: [productId],
    success_url: "https://yourapp.com/thanks?checkout_id={CHECKOUT_ID}",
    metadata: typeof visitorId === "string" && visitorId ? { bugotter_visitor_id: visitorId } : {},
  }),
});
const { url } = await res.json(); // send the customer here

This uses your app's own Polar token, the one that already creates checkouts (scope checkouts:write), not the read-only token you gave BugOtter.

Using the Polar SDK or a framework adapter? Pass the same metadata to its checkout call.

Shared checkout links you paste into a page aren't tagged automatically yet; that works for Stripe Payment Links only. The request format is from Polar's checkout API reference, checked Nov 4, 2026.

How MRR is counted for Polar

Net of tax

Revenue uses Polar's net amount: after discounts, before taxes.

So the VAT Polar collects as merchant of record isn't counted as yours.
  • What counts: active and past-due subscriptions. Trials count once they pay. One set to cancel counts until its period ends.
  • Amount: the subscription's current amount as Polar reports it, turned into a month. Yearly ÷ 12, and Polar's interval count is respected, so “every 3 months” is ÷ 3.
  • Refunds: subtracted from revenue. Partial refunds subtract only what was refunded.
  • Currencies stay separate, never converted.

Want to sanity-check an annual-plan-heavy month? Try the MRR calculator.

Read-only toward Polar

BugOtter never creates checkouts, issues refunds, cancels subscriptions or emails your customers. The token can't, and BugOtter's Polar code only reads.

Connecting and disconnecting are only in the app, not over the MCP server. Disconnecting deletes the Polar orders, subscriptions and products BugOtter copied, and leaves any other provider's numbers alone.

Polar next to the other payment providers

BugOtter reads four providers. Here's what each one gives you.

What BugOtter reads from each payment provider
FeatureStripePolarLemon SqueezyCreem
Access
The key you give BugOtterYesRestricted key, Read onlyYesOrganization token, read scopesPartlyAPI key (can't be limited)PartlyAPI key (can't be limited)
Revenue
Revenue after refundsYesYesYesYes
MRR and how it movedYesYesYesYes
Trials and when they endYesYesYesYes
Cancellations scheduled (leaving soon)YesYesYesYes
Why customers cancelledYesYesNoNo
Refunds on the day they happenedYesPartlyWith refunds:readPartlyOrders from the last 90 daysPartlyDated by the payment
Failed payment alertsYesNoYesYes
Linking and testing
Revenue by traffic sourceYesPayment Links: automatic. Your checkout: metadataYesCheckout metadataNoNot yetPartlySubscriptions, via checkout metadata
Test modeYesrk_test_ keys, marked Test modeNoNot yet (the sandbox is a separate API)YesTest-mode API keysYescreem_test_ keys, marked Test mode

Questions

Which scopes does the Polar token need?

products:read, orders:read and subscriptions:read. Add refunds:read so refunds show on the day they happened; without it they're dated by the order. Don't give it any write scope: BugOtter never needs one.

Does it work with the Polar SDK?

Yes. BugOtter only cares that the checkout carries metadata.bugotter_visitor_id. Pass the same metadata object to the SDK's checkout create call instead of using fetch. Polar copies checkout metadata to the order and the subscription it creates.

Does BugOtter count the tax Polar collects?

No. Polar is the merchant of record and handles sales tax and VAT. BugOtter uses each order's net amount, which Polar defines as after discounts and before taxes.

Can I try it with Polar's sandbox?

Not yet. Polar's sandbox runs on a separate API, and BugOtter only reads the production one. A sandbox token won't connect. The fastest check is a real $1 product you refund afterwards.

Why don't I see failed payment alerts for Polar?

BugOtter reads paid and refunded Polar orders, not failed charge attempts, so failed payment alerts aren't available for Polar yet. Past-due subscriptions still count in MRR and show on the Revenue page.

Can a coding agent change my Polar account through BugOtter?

No. The token only reads, and connecting or disconnecting Polar is only possible in the BugOtter app, never over MCP.

Polar takes the payments. BugOtter tells you which post earned them.

14 days of everything, no card needed. Plans from $10 a month, billed yearly.