Polar Changed How It Signs Webhooks. Here Is What It Means for Your Ghost Site

Close-up of hands in a suit signing a handwritten note with a black pen on a dark wooden tablePhoto: Vitor Monthay
On this page

If you connect Polar to Ghost through PayGlue, I can save you the reading time: nothing broke, and there is nothing for you to do. Something did change at Polar on 8 September 2026 though, and if a different tool sits between Polar and your Ghost site, that change may matter to you.

What changed

Polar signs every webhook it sends, so the receiver can check that it really came from Polar. Polar computes the signature with a key that it derives from the webhook secret you see in its dashboard, the one that starts with whsec_.

Until September, Polar derived that key one way. Since 8 September, secrets created in Polar follow the Standard Webhooks specification, and that spec derives the key differently: it base64-decodes the part after whsec_ first. An older secret skips that step, and the whole string is the key.

The two derivations produce different keys from the same secret. A receiver that picks the wrong one rejects every webhook as unsigned, and a rejected webhook is a purchase that never turns into a member.

Why it is easy to get wrong

Nothing on the webhook says which derivation Polar used, and the secret itself looks the same either way. So the receiver has to know in advance, or it has to guess.

Our adapter used to guess, from the shape of the secret. That was fine while there was only one way to derive the key. With two, the guess is a coin flip on every new connection, and when the flip lands wrong nothing makes a sound. The webhook arrives, the signature does not match, the adapter rejects the event, and the reader who just paid sees nothing at all.

What we did

The adapter now checks both derivations. It computes the signature once with the decoded key and once with the raw key, and accepts the webhook if either one matches. A connection you created in June still works, and so does one you created this week. If you rotate the secret next month, that works as well.

I put this into production on 5 September, three days before Polar’s change, so no PayGlue customer ever hit it. If you run your own installation from the open source repo, the fix ships with v0.6.0. An install still on v0.5.0 rejects any Polar webhook created after 8 September, so update before you rotate a secret or add a new webhook.

The second thing Polar now asks

When you create a webhook in Polar, it now asks which API version you want: 2026-04 or 2026-10. That question is new as well, and the docs did not say which one to pick.

I sat down and compared both specifications for the five events PayGlue reads: order created, order paid, subscription active, subscription canceled and subscription revoked. The fields we use are identical in both versions. So pick 2026-04. It is the version we test against, and the setup guide now says so. If Polar changes the shape of an event in a later version, I will write about it here.

How to tell which kind of secret you have

You cannot, at least by looking at it. Both kinds start with whsec_ and both continue with a string of letters and digits. The only difference is what Polar does with that string when it signs.

What you can do is test it. Send a test event from Polar’s webhook settings and read the receiver’s log. If a fresh secret produces a signature error while an old one passes cleanly, the receiver only knows the old derivation. If both pass, it handles the change.

What this looks like from the reader’s side

Like nothing, and that is the problem. A reader buys a membership on Polar’s checkout, gets Polar’s confirmation email, and comes back to your site expecting the paywall to open. It stays shut. As far as the reader can tell, the purchase worked and your site is broken. As far as Polar can tell, the webhook was delivered. The receiver, meanwhile, rejected an unsigned request, which is exactly what it should do with an unsigned request.

Three systems did their job and a reader is locked out anyway. Most payment integration failures I have seen look like this, and it is the reason every rejected event ends up on the events page in the dashboard, where you can see it and, once the secret is right, replay it.

If you use a different tool

Check whether it handles both derivations. Create a fresh webhook in Polar today, point it at your tool, and make a sandbox purchase. If the member shows up in Ghost, you are fine. If the tool logs a signature error instead, it is applying the old derivation to a new secret.

And if you are building your own receiver, do what I did and try both keys. It costs you one extra HMAC per webhook, and it spares you a silent outage on the day your provider changes its mind.

Photo by Vitor Monthay on Unsplash

Frequently asked

Do I need to change anything on my Polar connection?

No. Existing connections keep working, and a new one created after 8 September works too. PayGlue verifies against both derivations of the secret and accepts whichever matches.

Which API version should I pick when Polar asks?

2026-04. Polar now offers 2026-04 and 2026-10 when you create a webhook. For the five events PayGlue reads, the payloads are identical, and 2026-04 is the one we test against.

What is the Standard Webhooks specification?

A shared convention for how webhooks are signed and verified, used by several providers. The signing key is derived from the secret in a defined way. Polar's older secrets used a different derivation, which is why both have to be supported for a while.