DOCS LLMs

Selling software licenses on Lemon Squeezy

Selling software licenses on Lemon Squeezy

Connect a Lemon Squeezy store to LicenseSeat and orders for linked variants issue licenses; eligible email delivery sends them to the buyer. A refund revokes it. Subscriptions are supported: each renewal extends the license, a refunded renewal revokes it. Nothing to install: the connection is a webhook you create in your Lemon Squeezy 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 Lemon Squeezy, and give the connection a name. The page shows two values:

  • your webhook URL
  • your signing secret, which LicenseSeat generated for you

Copy both. Keep them private: anyone holding the secret could forge orders and mint licenses for your products.

2. Turn Test Mode off

Look at the bottom of Lemon Squeezy's left sidebar for the Test Mode switch. Make sure it is off before the next step.

Lemon Squeezy keeps test-mode and live-mode webhooks apart: a webhook created while Test Mode is on is only triggered by test orders, and one created with it off is only triggered by real orders. New stores start in Test Mode, so this is easy to miss. If you want a rehearsal as well, come back later and create a second webhook with Test Mode on (see step 5).

3. Create the webhook in Lemon Squeezy

Go to Settings → Webhooks and click the + button at the top right. Fill in:

Field Value
Callback URL your LicenseSeat webhook URL from step 1
Signing secret your LicenseSeat signing secret from step 1
Events tick order_created and order_refunded

If you sell subscriptions, also tick subscription_created, subscription_payment_success and subscription_payment_refunded.

Save. Lemon Squeezy accepts any secret of 6 to 40 characters; the one LicenseSeat generated is 40, so it fits as is.

order_created fires when an order is placed and that is what issues the license. order_refunded fires when you refund the order, fully or partially, and that revokes it. For subscriptions, subscription_payment_success arrives on every renewal and extends the license; subscription_payment_refunded revokes it. Every other event is ignored, so ticking more does no harm and unticking these does.

Tell LicenseSeat which license plan to issue for each Lemon Squeezy variant. On the connection page click Link Product, pick the plan, and enter the Variant ID.

To find it, open Store → Products in Lemon Squeezy, open the product's menu and choose Copy ID. If the product has several variants (say a personal and a studio edition) and each should issue a different license, use the variant's own Copy ID instead. A product with a single price still has exactly one variant, which Lemon Squeezy calls Default, and Copy ID gives you its number.

The Variant ID is a plain number, for example 123456. A long code with dashes taken from a checkout link is the variant's checkout slug, not its ID, and will not match. Test Mode products have their own IDs, separate from live ones: link the live ID for real sales.

5. Test it

Lemon Squeezy can only simulate events for Test Mode orders, and Test Mode webhooks are separate from live ones, so a rehearsal needs its own setup:

  1. Turn Test Mode on.
  2. Create a second webhook exactly like step 3 (same URL, same secret, same events).
  3. Publish a product in Test Mode (test-mode products are separate copies) and link its Variant ID in LicenseSeat too.
  4. Open the product's Share or Preview checkout and buy it with card number 4242 4242 4242 4242, any future expiry and any CVC.

Within a few seconds the LicenseSeat connection shows Active and a license appears under your product, owned by the email you typed at checkout.

To replay an event without buying again, open the test order in Lemon Squeezy, click Simulate event, pick order_created or order_refunded from the dropdown and click Trigger event. On a test subscription the same button offers subscription_created, subscription_cancelled, subscription_expired and the rest; renewal events (subscription_payment_*) can only be simulated once a real renewal has happened, so use a daily billing interval on the test product if you need them.

Every delivery, live or test, is listed under Recent deliveries on Settings → Webhooks, with the response code LicenseSeat returned and a Resend button.

Revoke the test licenses afterwards, or refund the test order.

What happens on each event

Lemon Squeezy event LicenseSeat
order_created Issues a license for the order's first item if its variant is linked, owned by the buyer's email, and emails the key. One license per order.
order_refunded Revokes the license issued for that order. Partial refunds revoke too.
subscription_created Sent together with order_created on the first payment of a subscription. LicenseSeat issues one license, not two, and remembers the subscription.
subscription_payment_success On a renewal (billing_reason is renewal), extends the license issued when the subscription started. The initial payment is ignored here because order_created already handled it.
subscription_payment_refunded Revokes the license attached to that subscription.
subscription_cancelled, subscription_expired, subscription_updated, anything else Ignored. A cancelled subscription is not renewed again; its license expires at its stored expiry. These events do not revoke immediately.

Good to know

  • Existing perpetual mappings. Subscription purchases still issue on a perpetual plan. Events that identify a subscription show a warning; cancellation does not revoke lifetime access. Existing perpetual licenses remain perpetual on renewal. Choose a finite plan for access that should expire, and review subscription access and recovery before changing existing mappings.

  • Finite subscription plans. Use a plan duration matching the billing interval. Initial purchases and free trials grant that duration; trial-end synchronization is not provided. Renewals extend to the invoice creation time plus the mapped plan duration, rather than importing the provider’s next billing date. Cancellation or a failed renewal leaves the stored expiry unchanged. Verify these dates in your rehearsal.

  • First item only. The order webhook names the first item; LicenseSeat uses its quantity when present and otherwise defaults to one. A subscription-first purchase defaults to one. Use separate orders with quantity one for predictable subscription fulfillment; later cart items are not fulfilled.

  • Renewals need history. A renewal only extends a license whose first purchase LicenseSeat already saw. Subscriptions that started before you connected cannot be matched automatically. A manual license with the same email still lacks subscription provenance; replay the initial purchase/subscription events or maintain access manually.

  • Signatures. Lemon Squeezy signs every request with your secret (HMAC-SHA256 of the raw body, sent in the X-Signature header). LicenseSeat rejects anything else with 401.

  • Retries. If LicenseSeat does not answer 200, Lemon Squeezy retries up to three more times with growing delays (about 5, 25 and 125 seconds), then gives up; you can still Resend from Recent deliveries. LicenseSeat answers immediately and processes the event in the background, and duplicate deliveries of the same event are recognised, so an order is never fulfilled twice.

  • Test orders. Orders placed in Test Mode carry test_mode: true and are treated like any other order once the test webhook exists. Revoke those licenses afterwards.

  • Custom checkout data. You do not need to pass anything through checkout[custom][...]: the order already names the variant, and that is what LicenseSeat matches on.

  • Changing providers. A new Stripe connection does not migrate Lemon Squeezy subscription provenance. Plan a separate migration and test renewals/refunds before switching.

Troubleshooting

  • The connection stays "Incomplete". No product is linked yet. Click Link Product on the connection page.
  • Real orders never arrive, test orders do. The webhook was created while Test Mode was on. Turn Test Mode off and create another webhook with the same URL, secret and events.
  • The event shows as "ignored" with "no active mapping". The order's variant ID does not match any linked product. Use Copy ID on the exact variant sold, and check you linked the live ID rather than a Test Mode one.
  • Recent deliveries shows 401. The signing secret in Lemon Squeezy does not match. Open the webhook there, paste the secret from the LicenseSeat connection page again, and Resend the delivery.
  • A refund did not revoke. The refund must be issued on the order (or subscription invoice) that issued the license. Refunds of orders placed before the connection existed cannot be matched.
  • A renewal did not extend. Either subscription_payment_success is not ticked on the webhook, or the subscription started before the connection existed.

References

These are Lemon Squeezy's own pages. If a label or menu in the Lemon Squeezy dashboard ever differs from this guide, they are the source of truth: