Selling software licenses on Polar.sh
Selling software licenses on Polar.sh
Connect a Polar organization to LicenseSeat and paid orders for linked products issue license keys; eligible email delivery sends them to the buyer. A subscription renewal extends existing access, and a successful refund revokes it. No code on your side: the connection uses a webhook endpoint you add in your Polar dashboard.
Setup takes about five minutes.
Before you start
Link the product to a license plan before testing. Automatic license emails require an eligible LicenseSeat plan, Settings → Organization → Email license keys to my customers enabled, and a valid buyer email. Issuance can succeed without sending an email. Use a separate test organization when you need complete separation: connections in one organization share product mappings by provider.
Read Email delivery, subscription expiry and event recovery for the common prerequisites and recovery procedure. Setup instructions remain available on the connection page after saving.
1. Create the connection in LicenseSeat
In the LicenseSeat dashboard sidebar open Connected platforms, click Connect Your First Platform (or Connect Another), choose Polar, and give the connection a name. The page shows your webhook URL. Copy it. Keep it private: only paste it into Polar.
2. Add an endpoint in Polar
In your Polar dashboard go to Settings → Webhooks and click Add Endpoint. Fill the form like this and save:
| Field | Value |
|---|---|
| URL | your LicenseSeat webhook URL from step 1 |
| Format | Raw |
| Events | tick order.paid and order.refunded |
order.paid fires when an order is collected, and that is what issues the license. On a subscription it also fires on every renewal, which extends the license. order.refunded fires when you refund the order, fully or partially, and that revokes the license.
Subscribe to both events above. refund.created and refund.updated are ignored: a created refund can still be pending or fail. Only order.refunded confirms a successful refund. See Polar’s refund guide.
3. Paste the endpoint's secret
Polar shows a Secret for the endpoint. It starts with whsec_. Copy it into the Signing Secret field on the LicenseSeat connection page and save.
Polar signs every webhook with this secret, and LicenseSeat rejects anything that is not signed with it. Polar uses a new signing scheme for secrets generated on or after 8 September 2026 UTC (Standard Webhooks); secrets created before and after that date both work with LicenseSeat, so there is nothing to migrate. If you regenerate the secret in Polar (Reset webhook endpoint secret), paste the new one here.
4. Link your products
Tell LicenseSeat which license plan to issue for each Polar product. On the connection page click Link Product, pick the plan, and enter the Product ID: in your Polar dashboard open Products, click the ⋯ menu in front of the product and choose Copy Product ID. It is a UUID such as 1dbfc517-0bbf-4301-9ba8-555ca42b9737. Pasting the product page URL also works; LicenseSeat keeps the ID.
A Polar checkout sells one product per order, so there is nothing to choose between lines. If an order contains several prices for a product, one mapping on the Product ID covers them. To issue a different plan per price, link the Price ID instead; LicenseSeat checks the Product ID first, then each price on the order.
Selling a subscription? Initial access uses the mapped license plan’s duration, including trial signups; it does not automatically match the trial end or first billing-period end. Later paid renewals extend finite licenses using Polar’s current period end, without shortening access already granted. Pick a finite plan deliberately and test the actual expiry. A perpetual plan still issues perpetual access with a Subscription plan warning; cancellation does not end that access.
Seats: initial fulfillment issues one license per seat (up to 1,000), including subscription trials, all owned by the buyer. Later seat increases, decreases, plan changes and member assignments are not synchronized. Renewals extend the existing licenses without changing their count. Manage additional licenses and revoke excess licenses manually in LicenseSeat. Licenses created manually do not automatically follow the subscription’s renewals or refunds; manage their expiry and revocation too. See Polar’s seat model.
5. Test it
Polar's sandbox is a completely separate environment with its own account, organization, products, webhooks and secret. No money moves in Polar’s sandbox. Its webhooks still create real licenses and can send emails in the connected LicenseSeat organization.
For a rehearsal:
- Create a second connection in LicenseSeat for the sandbox.
- In the sandbox dashboard, add the endpoint the same way as in steps 2 and 3, with the sandbox's own secret.
- Link the sandbox product's ID to a plan.
- Buy the product through its checkout link with card
4242 4242 4242 4242, any future expiry and any CVC.
After processing, verify the connection is Active and check the licenses under your product: quantity, owner, expiry and email delivery separately. For subscriptions, also test a renewal; for trials, verify the initial plan duration is acceptable. Then open the order in the sandbox dashboard, scroll to Refunds and click Refund order. The license is revoked. Revoke or delete the test licenses when you are done.
Every delivery, live or sandbox, is listed on the endpoint's page under Settings → Webhooks, with its payload, its response, and a Redeliver button.
What happens on each event
| Polar event | LicenseSeat |
|---|---|
order.paid, billing reason purchase |
Issues a license for the linked product, owned by the customer when an email is present. Eligible email delivery sends the key. Initial seat-based orders issue one license per seat. |
order.paid, billing reason subscription_create |
Issues licenses for the initial seat count and records the subscription. Initial expiry uses the mapped plan duration, including trials. |
order.paid, billing reason subscription_cycle |
Extends existing finite licenses using the new period end without shortening existing access; perpetual licenses stay perpetual. Does not change seat quantity. |
order.paid, billing reason subscription_update or subscription_meter_cycle |
Ignored. A prorated plan change or a usage invoice is not new access. |
order.created, order.updated |
Ignored unless the order is already paid, in which case they behave like order.paid. An order never issues twice. |
order.refunded |
Revokes all licenses issued for that order. Any refund does this, including a partial one. |
refund.created, refund.updated |
Ignored, including pending and failed refunds. Subscribe to order.refunded for successful refunds. |
subscription.canceled, subscription.revoked, subscription.paused, subscription.past_due |
Ignored. Finite access ends at the license's stored expiry when no renewal arrives; perpetual access remains. |
| Anything else | Ignored. Extra events do not replace the two required events. |
Good to know
- No buyer email. Polar customers almost always have an email, but if an order arrives without one the license is still issued, without an owner. An organization owner/admin can use Assign customer on the license page and optionally send the key if email delivery is enabled.
- Cancellations do not revoke. Like every other platform LicenseSeat connects to, a canceled subscription keeps its license until its stored expiry. Initial expiry uses the plan duration, not the provider’s first period or trial end. Perpetual licenses have no expiry. A successful refund revokes immediately.
- Retries and disabling. Polar retries a failed delivery up to ten times with exponential backoff, and disables the endpoint after ten consecutive failures, emailing everyone in your organization. LicenseSeat acknowledges receipt before background processing; check processing status separately. If Polar disables delivery, fix the cause (for example a changed secret or unavailable endpoint) and re-enable the endpoint from Settings → Webhooks.
- Replay protection. A delivery whose timestamp is more than five minutes old is rejected, as the Standard Webhooks specification recommends. Polar re-signs redeliveries, so Redeliver can pass the timestamp check with the current correct secret.
- Duplicates. Polar's
webhook-ididentifies each message across retries, so a delivery received twice is processed once. - Polar's own license keys. If the product also has a Polar License keys benefit, both keys are issued. Remove the benefit from the product once LicenseSeat is connected so buyers receive a single key.
Troubleshooting
- The connection stays "Incomplete". The signing secret is missing, or no product is linked yet. Both are on the connection page.
- The event shows as "ignored" with "no active mapping". The order's Product ID does not match any linked product. Copy the ID again with Copy Product ID, and check that you linked the production product, not the sandbox one.
- Polar shows a failed delivery with status 401. The signing secret does not match. Copy it again from the endpoint's page in Polar. If the endpoint was disabled after repeated failures, re-enable it after fixing the secret.
- Polar shows a failed delivery and the timestamp is old. The delivery was replayed more than five minutes after it was signed. Use Redeliver in Polar, which signs a fresh copy.
- A refund did not revoke. Check that the refund succeeded and that
order.refundedis selected. The order must correlate with an issued license or its subscription; refunding a renewal can revoke the whole subscription, including access paid for by later orders. If no license matches, the refund is ignored and retained, without repeated failed jobs. A later matching purchase or recovered history can resolve it automatically; manual retry remains available. Recover the missing purchase or revoke manually; see the recovery guide.
References
These are Polar's own pages. If a label or menu in the Polar dashboard ever differs from this guide, they are the source of truth:
- Setup webhooks — adding an endpoint under Settings → Webhooks, the Raw format, the secret, and choosing events.
- Webhook events — every event, including
order.paidandorder.refunded, and the sequences fired on renewals and cancellations. - Handle and monitor deliveries — signature validation for both secret generations, retries, the ten-second timeout, and endpoint disabling.
- Standard Webhooks specification — the
webhook-id,webhook-timestampandwebhook-signatureheaders. - Orders — order statuses and the
billing_reasonvalues. - Refunds — issuing full and partial refunds from an order's page.
- Sandbox environment — the separate test environment and the
4242 4242 4242 4242card. - Order webhook payload — the fields LicenseSeat reads (
product_id,customer.email,total_amount,subscription.current_period_end,seats). - Seat-based pricing — Polar seats and member assignments; the LicenseSeat connector only automates the initial quantity.
- Versioned webhook schema — the reviewed Order and embedded OrderSubscription contract.