Skip to content

Setup

Meta Ads and Polar: match each Polar payment to its Meta campaign

To see which Meta campaigns produced your Polar payments, tag your ad URLs with UTM parameters, connect Polar to Thexly with an organization access token, and get the visitor's session into each Polar checkout. The last part needs a small change on your site, because Thexly doesn't tag Polar checkout links for you.

That's the main difference from Stripe. On a Stripe Payment Link placed on your page, the script adds the session by itself. On a Polar link it doesn't, so a plain polar.sh link arrives at Polar with nothing that points back to the visit.

The three steps in short:

1. Tag the ad clicks in Meta Ads Manager.

2. Connect Polar with an Organization Access Token that has all six scopes.

3. Pass the session to Polar, in checkout metadata or through a tracked return page.

Want to see it work on your own account?

Start a 14-day free trial, no card needed, then connect Polar as shown in step 2.

No card required.

What you need

Access to the ad in Meta Ads Manager.

Access to your Polar organization's settings, so you can create a token (Thexly Polar guide).

The Thexly tracking script on your site (docs).

A way to change the buy button or checkout code on your site. Step 3 needs it.

What we checked, and how

On 23 October 2026 we read Polar's docs on checkout links, its checkout API reference and its access token page, next to Meta's help page on URL parameters. We also went through how Thexly reads Polar orders with our engineering team. Three things stood out:

1. Polar carries metadata from the checkout to the order. Its API reference says "Metadata set on the checkout will be copied to the resulting order and/or subscription" (Polar API reference). That's the field Thexly reads.

2. Polar checkout links take a reference_id in the URL. Polar says these parameters "are automatically attached to the generated Checkout Session metadata" (Polar docs).

3. Polar can put the checkout ID in your success URL. If you add checkout_id={CHECKOUT_ID}, "Polar will substitute it with the actual Checkout Session ID at redirect time" (Polar docs).

Step 1: tag the ad clicks in Meta

From Meta's help page on URL parameters (Meta help):

1. Open the ad in Ads Manager and go to Tracking.

2. Type your parameters into URL parameters, or use Build a URL parameter.

3. Click Apply.

An illustrative example: utm_source=facebook&utm_medium=paid&utm_campaign=spring-launch. Pick a campaign name you can read at a glance, because Thexly shows utm_campaign as it arrives. Keep utm_source the same every time, all lowercase, so one source doesn't split into several rows.

Meta says the parameter only applies to the first page someone lands on. If they leave and come back on their own later, the URL parameter is lost. That's one more reason to carry the session to Polar in step 3.

When the visitor lands, Thexly reads the UTM tags and click IDs such as fbclid from the page URL. Nothing Polar-specific changes here.

One setting to check: if your Thexly script has data-exclude-search turned on, it drops all UTM tags and click IDs such as fbclid, so Thexly can't tie those visits to your campaigns. Keep it off on pages your ads link to.

Step 2: connect Polar

Polar's token page explains where tokens live (Polar docs):

1. In Polar, open Settings for your organization, scroll to Developers and click New Token.

2. Give it a name you'll recognise and set an expiration date.

3. Select these six scopes, as listed on Thexly's settings screen: Checkout Read, Orders Read, Organization Read, Products Read, Subscription Read and Webhook Write.

4. Create the token and copy it. It starts with polar_oat_.

5. Paste it into Thexly under Settings → Payments → Polar.

Use an organization token. A personal token (polar_pat_) is turned down. If a scope is missing, Thexly shows an error when you save and lists the six it needs, so you'll know right away.

When the token is accepted, Thexly creates a webhook in Polar called "Thexly revenue attribution". That's the only thing it writes to Polar. Reconnecting updates that webhook, and disconnecting removes it.

Step 3: pass the session to Polar

Pick the method that fits how your buy button works. You can use both.

Method A: put the session in the checkout metadata. On the page with the buy button, call window.thexly.getSessionId() to get the visitor's session. Then:

- Polar checkout link: add the value to the link as reference_id before the visitor goes to Polar. Thexly reads reference_id only when it starts with s_.

- Checkout you create with Polar's API: set metadata.thexly_session_id to the value.

Thexly reads those two keys from the Polar order or subscription and nothing else. A visitor ID in metadata isn't read, and neither is any other reference_id. Polar's limits apply: up to 50 pairs, keys up to 40 characters, text values up to 500 characters (Polar API reference).

Method B: send the buyer back to a tracked page. Set the checkout's success URL to a page on your site that runs the Thexly script, and add checkout_id={CHECKOUT_ID} to it. When the buyer lands there, Thexly records that visit with the checkout ID, and the Polar order matches when the IDs line up.

It's that tracked visit that counts, not the redirect itself. So the buyer has to reach the page. Leave order_id out of the URL, because Thexly then treats it as a Lemon Squeezy return and skips checkout_id. And it won't work if you've turned Payment URL detection off under Advanced in Attribution settings.

What doesn't work on its own: the buyer's email. A Polar payment matches by email only if you linked that email to the visitor earlier with thexly.identify().

One click, one payment

An illustrative example, not a customer. Someone clicks your spring-launch ad and lands on your pricing page with the UTM tags and fbclid in the URL. Your buy button adds their session to the Polar checkout link as reference_id. They pay €40.

Polar sends order.paid with the reference_id on the order. Thexly ties the payment to that visit, and the €40 shows next to spring-launch. The click ID stays on the visit in Thexly. It's used once the payment is tied to the session, and it isn't sent to Polar or Meta.

Setting this up on several client sites?

Each client gets its own workspace: Thexly for agencies.

What Thexly imports, and when

On connect, Thexly imports paid orders from the last 30 days and every subscription, whatever its age. It does this once. After that, new payments and changes arrive by webhook. Thexly doesn't poll Polar.

The webhook listens for order.paid, order.refunded, and these subscription events: created, active, updated, canceled, uncanceled, revoked and past_due. Thexly doesn't use checkout events.

Subscriptions and renewals

A renewal reaches Thexly as an order.paid, the same as a first payment. The subscription events update status, dates and seats. They don't add revenue.

Once a customer's first payment is tied to a visit, later charges for the same Polar customer or subscription reuse that visitor. So getting the first checkout right is what carries the renewals.

Thexly stores a subscription's amount as its MRR figure without turning yearly or weekly plans into a monthly figure. Keep that in mind if you sell more than one billing interval.

Refunds and currency

When Polar sends order.refunded, Thexly lowers the revenue on the original order by the refunded amount, tax included. It keeps one refund figure per order, so a later refund event replaces it rather than adding another. Polar disputes and chargebacks aren't recorded.

Thexly divides every Polar amount by 100 and converts it to your site's reporting currency. A payment with no currency is read as USD. This isn't the same as Stripe, where currencies without cents, like JPY, are handled separately. If you sell in one of those, check a test payment's amount.

Check it works

1. Open your ad's destination URL with the UTM parameters added.

2. Check that the visit shows in your Thexly dashboard (docs).

3. Buy through the same button, with method A or B in place.

4. Confirm the payment appears with the campaign name from step 1.

If the payment shows up with no campaign, look at step 3 first. A plain Polar link with no session on it is the usual gap.

What Thexly does and doesn't do

Thexly matches each Polar payment to the visit that led to it, so you can see which campaigns brought in money that was paid. There's no Meta account to connect.

It doesn't send anything to Meta or Polar about the click, and it doesn't change your ads or your Polar checkouts. You add the session yourself, as in step 3.

Want a quick sense of how far Meta's figures sit from what was paid? The platform vs payment gap calculator works it out from three numbers. More reading: Meta Ads reporting.

Which do you use on your site today, Polar checkout links or checkouts built with the API? That decides whether you start with reference_id or thexly_session_id.

Ready to try it?

Start free and send one test payment to see it land.

No card required.

FAQ

Questions & answers

Sources