# Lemon Squeezy Webhooks

## When to Use This Skill

* How do I receive Lemon Squeezy webhooks?
* How do I verify the Lemon Squeezy `X-Signature` header?
* Why is my Lemon Squeezy webhook signature verification failing?
* How do I handle `order_created`, `subscription_created`, or `license_key_created` events?
* How do I get my own user ID from a Lemon Squeezy webhook (`custom_data`)?

## Verification (core)

Lemon Squeezy sends `X-Signature`: the lowercase **hex** HMAC-SHA256 of the **raw request body**, keyed with the signing secret you entered when creating the webhook. There is no timestamp, no prefix and no SDK helper. The secret is a plain 6-40 character string, used as-is (not base64-decoded).

Node:

```javascript
const crypto = require('crypto');

function verify(rawBody, signatureHeader, secret) {
  const expected = Buffer.from(crypto.createHmac('sha256', secret).update(rawBody).digest('hex'), 'utf8');
  const received = Buffer.from(signatureHeader || '', 'utf8');
  return expected.length === received.length && crypto.timingSafeEqual(expected, received);
}

```

Python:

```python
import hmac, hashlib

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected.encode(), (signature_header or "").encode())

```

> **For complete handlers with route wiring, event dispatch, and tests**, see:
> 
> * [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/lemon-squeezy-webhooks/examples/express/)
> * [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/lemon-squeezy-webhooks/examples/nextjs/)
> * [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/lemon-squeezy-webhooks/examples/fastapi/)

## Common Event Types

The event name is in the `X-Event-Name` header and in `meta.event_name` in the body.

| Event | Description |
| --- | --- |
| `order_created` | New order placed (also sent alongside `subscription_created` and `license_key_created`) |
| `order_refunded` | Full or partial refund on an order |
| `subscription_created` | New subscription created |
| `subscription_updated` | Any subscription change; works as a catch-all |
| `subscription_cancelled` | Cancelled; stays active until the end of the grace period |
| `subscription_payment_success` | Subscription payment succeeded (payload is a subscription invoice) |
| `license_key_created` | License key issued from a new order |

Also available: `customer_updated`, `subscription_resumed`, `subscription_expired`, `subscription_paused`, `subscription_unpaused`, `subscription_payment_failed`, `subscription_payment_recovered`, `subscription_payment_refunded`, `license_key_updated`, `affiliate_activated`.

> **For the full event reference**, see [Lemon Squeezy event types](https://docs.lemonsqueezy.com/help/webhooks/event-types)

## Important Headers

| Header | Description |
| --- | --- |
| `X-Signature` | Hex HMAC-SHA256 of the raw body |
| `X-Event-Name` | Event type, e.g. `order_created` |
| `Content-Type` | `application/json` (JSON:API resource object) |

## Gotchas

* Payloads are JSON:API: the resource is `data` (`data.id`, `data.attributes`), and your checkout `custom_data` is in `meta.custom_data`.
* Respond `200` to acknowledge. Any other status is retried up to 3 more times with exponential backoff (about 5s, 25s, 125s).
* There is no delivery ID or timestamp header, so make handlers idempotent (for example key on event name + `data.id` + `data.attributes.updated_at`).
* The same purchase fires several events (a subscription sale sends `order_created`, `subscription_created`, `subscription_payment_success`), and ordering is not guaranteed.

## Environment Variables

```bash
LEMON_SQUEEZY_SIGNING_SECRET=your_signing_secret   # Entered when creating the webhook (6-40 chars)

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 lemon-squeezy --path /webhooks/lemon-squeezy

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/lemon-squeezy-webhooks/references/overview.md) - Lemon Squeezy webhook concepts and events
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/lemon-squeezy-webhooks/references/setup.md) - Dashboard configuration guide
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/lemon-squeezy-webhooks/references/verification.md) - Signature verification details