Documentation Menu

Lemon Squeezy Webhook Not Working? Production Debugging Checklist

Problem Introduction

Orders or subscription updates happen, but your app state does not change.

Why It Happens

  • Wrong endpoint URL in live mode (test vs production URLs differ)
  • Signing secret mismatch — test secret used in production env
  • Slow handler causing Lemon Squeezy to retry and create duplicate writes
  • No idempotency key tracking for retried events

Step-by-Step Fix

  1. 1In Lemon Squeezy Dashboard → Settings → Webhooks, verify your live endpoint URL.
  2. 2Confirm LEMONSQUEEZY_SIGNING_SECRET in your production env matches the dashboard value exactly.
  3. 3Validate the signature against raw body bytes — never against parsed JSON.
  4. 4Acknowledge promptly after verification and a durable handoff; process slow business logic asynchronously.
  5. 5Replay one failed event from the Lemon Squeezy dashboard and watch your logs.

Working Code

Examples for common stacks. Adapt them to your provider contract and framework.

javascript
const crypto = require('crypto');

app.post('/webhooks/lemonsqueezy', express.raw({ type: 'application/json' }), async (req, res) => {
  const sig = req.headers['x-signature'];
  if (!sig) return res.status(400).send('Missing signature');

  const expectedSig = crypto
    .createHmac('sha256', process.env.LEMONSQUEEZY_SIGNING_SECRET)
    .update(req.body)
    .digest('hex');

  const received = Buffer.from(sig, 'hex');
  const expected = Buffer.from(expectedSig, 'hex');
  if (received.length !== expected.length || !crypto.timingSafeEqual(received, expected)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);
  const eventName = event.meta?.event_name;
  const orderId   = event.data?.id;

  // Idempotency check — store processed event IDs
  if (await isProcessed(orderId)) return res.status(200).json({ duplicate: true });

  await enqueueLemonEvent(eventName, event);
  res.status(200).json({ received: true });
});

Common Mistakes

  • Using test secret in production
  • Synchronous DB-heavy work before response
  • Not storing provider event IDs for deduplication

Debugging Workflow

Provider delivery logs → app ingress logs → signature check → billing state update → replay verification.

Preventive Best Practices

  • Track retry spikes by event type
  • Use deterministic idempotency keys for writes
  • Alert on sudden delivery failure bursts

Works with webhooks and other async event systems (including AI callbacks). Instead of guessing, inspecting the exact payload and headers can help debug faster.

Try the free webhook tester

Was this page helpful?

Your feedback helps us improve the docs.