Indie SaaS on Polar · ~20 min
Connect Polar to RevFast
Create a Polar organization token with the right scopes, paste it in RevFast (we register the webhook), then pass ros_visitor_id when you create checkouts in your app.
- 01
Which Polar connection is this?
There are two completely different Polar flows in RevFast. Do not mix them up. A) Attribute sales on YOUR product (this guide) • Where: RevFast → Settings → Payments → Polar • Webhook: https://YOUR-REV-FAST-HOST/api/webhooks/polar?site=YOUR_SITE_ID&key=INGEST_KEY • Your job: Polar token in RevFast + metadata on YOUR checkout code • RevFast job: verify token, register webhook, match order.paid to ros_uid B) Bill for RevFast subscriptions (self-hosted operators only) • Where: Admin → Settings → Polar • Webhook: https://YOUR-HOST/api/billing/webhook?provider=polar • RevFast creates plan products, opens checkout from in-app Billing, unlocks accounts • See “Admin billing token scopes” at the end of this guide Everything below is (A) — your product’s Polar sales in your analytics workspace.
- 02
Step 1 — Install tracking on your live site
RevFast must see pageviews before checkout. The tracker sets a first-party cookie named ros_uid — that value is how we tie a sale to the last visit. 1. Sign in to RevFast → Settings → Site. 2. Copy the script tag and paste it just before </head> on every public page (homepage, pricing, blog, app shell — not only checkout). 3. Publish or deploy your site. 4. Open the homepage in a normal browser window (not a blocker that strips cookies). 5. In RevFast, wait until tracking shows at least one pageview (usually within a few seconds). If ros_uid never appears, fix tracking before connecting payments. A webhook alone cannot guess which visitor paid.
- 03
Step 2 — Create or open your Polar organization
Polar is organization-scoped. Tokens, products, and webhooks belong to one org. 1. Go to https://polar.sh and sign in. 2. If you do not have an org yet: create one (name + slug). This is the merchant account that sells your SaaS. 3. In the Polar dashboard, use the organization switcher (top) and select the org that owns your products. All following steps must be done while that org is selected. 4. Create at least one Product in Polar (Catalog) with a price — you will reference its product ID when you build checkout in your app. RevFast does not create your catalog for flow (A). Sandbox vs live • Live: dashboard at polar.sh, API https://api.polar.sh, token prefix polar_oat_… • Sandbox: separate environment for test cards — use sandbox org, sandbox token, and sandbox product IDs together. Do not mix sandbox products with a live token. Official docs: https://polar.sh/docs/integrate/oat
- 04
Step 3 — Create an organization access token (OAT)
Use an Organization Access Token — not a personal access token in the browser, and never paste tokens in frontend code. Where to click (exact path): 1. Polar dashboard → make sure your organization is selected 2. Organization Settings (gear for the org, not your user profile) 3. Scroll to Developers 4. Click New Token Fill in the form: • Name: something you will recognize later, e.g. “RevFast webhooks” or “RevFast + my API server” • Expiration: your choice (long-lived is common for server integrations) • Scopes: see the next step — wrong scopes are the #1 reason Connect fails After Create: • Polar shows the token once (starts with polar_oat_…). Copy it immediately into a password manager or your server env. • If you leak it, revoke it in Polar and create a new one.
- 05
Step 4 — Scopes to select (two tokens explained)
Polar scopes use names like webhooks:write (see Polar → Authentication docs). RevFast only uses a subset of the Polar API for flow (A). ── Token you paste into RevFast (Settings → Payments → Polar) ── This token is ONLY used server-side by RevFast to verify your org and register the attribution webhook. RevFast does not create your product checkouts with this token. | Scope | Turn ON? | Why | | organizations:read | Yes (required) | RevFast calls GET /v1/organizations when you click Connect to prove the token works and show your org name. | | webhooks:write | Yes (required) | RevFast calls POST /v1/webhooks/endpoints to register your webhook when RevFast is on public https. | | webhooks:read | Optional | Not required for Connect. Helpful if you inspect endpoints in Polar’s UI. | | orders:read | No | RevFast does not poll orders. Revenue comes from webhooks (order.paid). | | checkouts:write | No | RevFast does not open your customer checkouts. You do that in your app (below). | | products:write | No | RevFast does not create your Polar catalog in flow (A). | Minimum checklist for RevFast Connect: organizations:read + webhooks:write. ── Separate token for YOUR backend (your Next.js / API server) ── When your app calls polar.checkouts.create, use a different OAT (or the same one if you combine scopes) on your server — never in the browser: | Scope | Turn ON? | Why | | checkouts:write | Yes | Create checkout sessions and redirect buyers to Polar. | | products:read | Yes | Reference product IDs you created in Polar. | | checkouts:read | Optional | Poll checkout status on thank-you pages. | You can use one OAT with organizations:read + webhooks:write + checkouts:write + products:read for both RevFast and your server, or split “RevFast-only” vs “app-only” tokens for least privilege. ── Admin → Settings → Polar (flow B only) ── If you self-host RevFast and charge for RevFast plans, that token additionally needs: organizations:read, webhooks:read, webhooks:write, products:read, products:write, checkouts:read, checkouts:write so RevFast can auto-create plan products, register /api/billing/webhook, and open checkout from Billing.
- 06
Step 5 — Connect in RevFast and what we do for you
1. RevFast → Settings → Payments → Polar. 2. Paste the polar_oat_… token (flow A token above). 3. Optional: Organization slug — label only; attribution does not depend on it. 4. Click Connect. What RevFast does automatically when you Connect (public https host): 1. Calls Polar GET /v1/organizations with your token → if this fails, you likely missed organizations:read or picked the wrong org token. 2. Creates a workspace-specific webhook URL: https://YOUR-HOST/api/webhooks/polar?site=YOUR_SITE_ID&key=RANDOM_INGEST_KEY The site and key query params are required — do not strip them. 3. Calls Polar POST /v1/webhooks/endpoints with: • format: raw (JSON) • events: order.created, order.paid, order.updated, order.refunded 4. Saves the signing secret Polar returns (Standard Webhooks) encrypted on the server. 5. Shows Connected with your Polar org name. What RevFast does NOT do: • Does not create your Polar products or prices • Does not change your website checkout button • Does not read your Polar dashboard orders on a schedule • Does not expose your full token back to the browser after save RevFast registers webhooks on your public HTTPS app URL (https://revfast.app or NEXT_PUBLIC_APP_URL on self-host). • Click Connect — we call the provider API and register the webhook when the URL is valid https. • If auto-registration fails, copy the Webhook URL from Settings → Payments and add it manually in the provider dashboard. If step 3 did not run (missing webhooks:write or NEXT_PUBLIC_APP_URL not set), add the webhook manually in the next step.
- 07
Step 6 — Manual webhook (if Connect did not register it)
Do this when Connect verifies the token but Polar did not auto-register the endpoint. 1. Copy the full Webhook URL from RevFast Settings → Payments (after Connect). 2. Polar → Organization Settings → Webhooks → Add endpoint (or Developers → Webhooks depending on Polar UI version). 3. URL: paste RevFast’s URL exactly, including ?site= and ?key=. 4. Format: Raw — not Slack, not Discord. 5. Events — at minimum subscribe to order.paid. RevFast also listens for order.refunded and ignores unpaid order.created / order.updated. 6. Save → copy endpoint signing secret → RevFast → Payments → Polar → Webhook secret field → save again. After a test payment, open the endpoint’s delivery log in Polar: • HTTP 200 = RevFast accepted and verified the payload • 401 = webhook secret mismatch in RevFast • 404 = wrong URL or missing ?site= / ?key= query params
- 08
Step 7 — Your app: create checkout with metadata
RevFast only attributes sales if metadata.ros_visitor_id is on the Polar checkout. Polar copies checkout metadata onto the order; the order.paid webhook payload includes it. Rules: • Read ros_uid from document.cookie on your site (see code). • Pass metadata on the server when you call checkouts.create — same request that redirects the user to Polar. • Buyer should have visited a page with tracker.js before checkout (same browser, normal cookies). Use YOUR backend token (checkouts:write + products:read), not the RevFast UI.
// Read the RevFast visitor cookie (set by tracker.js on your marketing site) const ros_visitor_id = document.cookie.match(/(?:^|; )ros_uid=([^;]*)/)?.[1]?.trim() || ""; const ros_session_id = document.cookie.match(/(?:^|; )ros_sid=([^;]*)/)?.[1]?.trim() || ""; // Your API route (Node) — env POLAR_OAT with checkouts:write import { Polar } from "@polar-sh/sdk"; const polar = new Polar({ accessToken: process.env.POLAR_OAT }); export async function POST(req: Request) { const { productId } = await req.json(); const cookieHeader = req.headers.get("cookie") || ""; const ros_visitor_id = cookieHeader.match(/(?:^|;\s*)ros_uid=([^;]*)/)?.[1]?.trim() || ""; const ros_session_id = cookieHeader.match(/(?:^|;\s*)ros_sid=([^;]*)/)?.[1]?.trim() || ""; const checkout = await polar.checkouts.create({ products: [productId], successUrl: "https://yoursite.com/thanks?checkout_id={CHECKOUT_ID}", metadata: { ros_visitor_id, ros_session_id }, }); return Response.json({ url: checkout.url }); } - 09
Step 8 — What happens when someone pays
End-to-end (flow A): 1. Visitor loads your marketing site → tracker.js sets ros_uid cookie. 2. Visitor clicks Buy → your server creates Polar checkout with metadata.ros_visitor_id = that cookie value. 3. Visitor pays on Polar’s hosted checkout. 4. Polar sends order.paid (Raw JSON) to your RevFast webhook URL. 5. RevFast verifies the Standard Webhooks signature with the stored secret. 6. RevFast reads metadata.ros_visitor_id (or ros_uid alias) from the order payload. 7. RevFast records revenue on Revenue and ties it to that visitor’s pages and journeys. If step 6 is missing (empty metadata), the sale may still appear as revenue but without visit attribution — fix checkout metadata, not RevFast tracking.
- 10
One Polar token on revfast.app (analytics + billing)
Yes — you can use the same Polar organization and the same polar_oat_ token for both charging RevFast customers and showing those sales in your own RevFast analytics. They are two hooks in the app, not two Polar accounts. What you are doing on revfast.app • Marketing/auth pages already load tracker.js (ros_uid cookie). • Admin → Settings → Polar: charges customers who click Subscribe on Billing (flow B). Token stays saved here for products, checkouts, and /api/billing/webhook?provider=polar. • Settings → Payments → Polar (site revfast.app): registers a second webhook for analytics Revenue (flow A). Paste the same polar_oat_ token and click Connect — RevFast does not keep this copy of the token; it only registers /api/webhooks/polar?site=…&key=… and stores the webhook signing secret. One token — combine scopes Use a single OAT with: organizations:read, webhooks:read, webhooks:write, products:read, products:write, checkouts:read, checkouts:write. Two webhooks on the same Polar org (both OK) 1. https://revfast.app/api/billing/webhook?provider=polar — unlocks plans (Admin saves this secret). 2. https://revfast.app/api/webhooks/polar?site=revfast.app&key=… — attributed revenue (Payments saves a different secret). Checkout metadata When someone subscribes from in-app Billing, RevFast passes ros_visitor_id from their ros_uid cookie into Polar checkout metadata (when the cookie exists). That lets webhook (2) tie the payment to the same visitor who browsed /pricing. Setup checklist for you 1. Admin → Settings → Polar: paste token → Save (billing webhook registers). 2. Your workspace site URL = revfast.app (Settings → Site). 3. Settings → Payments → Polar: paste the same token → Connect (analytics webhook registers). 4. Visit /pricing in a normal browser, then complete a test Subscribe — check Revenue and Polar webhook logs (both endpoints should get events). You do NOT need a second Polar org or a second product catalog. Flow B creates RevFast plan products; flow A only listens for paid orders on your site id. If Connect in step 3 says the webhook already exists with the same URL, you are done. If Polar shows two endpoints with different secrets, each must match the secret in Admin vs Payments respectively.
- 11
Admin billing (flow B) — extra scopes & webhook
Only if you operate a self-hosted RevFast and customers pay you for RevFast plans via Polar: • Token location: Admin → Settings → Polar (not Settings → Payments). • Webhook URL (fixed): https://YOUR-HOST/api/billing/webhook?provider=polar • On Save, RevFast: - Lists or creates POST /v1/webhooks/endpoints for billing events (checkout.updated, order.paid, order.refunded, subscription.*) - Creates Polar products/prices for each plan SKU when someone subscribes - Opens Polar checkout from in-app Billing and unlocks the workspace when order.paid verifies Required scopes for flow B: organizations:read, webhooks:read, webhooks:write, products:read, products:write, checkouts:read, checkouts:write. Your customers’ own Polar stores still use flow (A) in their workspace Settings → Payments.
- 12
Step 5 — Run a test payment and check Revenue
1. Use the provider's test mode (Stripe test cards, Polar sandbox, Razorpay test keys, etc.). 2. Complete a real checkout flow on your site — the same path a customer uses. 3. In RevFast, open Revenue (or Customers). Within a minute you should see the order amount next to the pages that visitor viewed. Still empty? • Open Settings → Payments and confirm the integration shows Connected. • In the provider dashboard, open Webhooks → recent deliveries. Look for HTTP 200 from RevFast, not 401/404/500. • Confirm checkout sent metadata.ros_visitor_id (or the provider-specific field in this guide) and that the buyer had ros_uid set before paying. • Ad blockers and cross-domain checkout (pay on another domain without the snippet) break attribution — keep checkout on the same site that runs the tracker, or pass the id explicitly.
- 13
Polar troubleshooting
Connect failed / “Polar rejected that token” → Add organizations:read. Confirm org switcher matches the org that owns the token. Connect OK but “webhook not registered” → Add webhooks:write and use public https RevFast URL, or add webhook manually. Polar deliveries 401 → Re-copy signing secret from Polar endpoint into RevFast Payments → Polar → Webhook secret. Revenue without journey → metadata.ros_visitor_id empty at checkout — cookie missing or checkout created server-side without forwarding Cookie header. Used polar_at_ personal token → Create polar_oat_ organization token instead. Wrong environment → Sandbox product IDs require sandbox token and sandbox webhook; live requires live.
Install with AI
Paste into Cursor or Copilot — swap in your real Site ID first.
Polar + RevFast flow (A) — attribute my product sales. Polar org: create org, select it, Settings → Developers → New Token polar_oat_. RevFast Payments token scopes: organizations:read + webhooks:write (minimum). Separate app server token: checkouts:write + products:read for checkouts.create. RevFast Connect: verifies GET /v1/organizations, POST /v1/webhooks/endpoints (raw, order.paid etc.), saves signing secret. My app: checkouts.create metadata ros_visitor_id from ros_uid cookie. Webhook URL must include ?site= and ?key=. Test order → Revenue + Polar delivery 200.
Related
Stuck? Email hello@revfast.app or open onboarding → Need help?