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. 4Return HTTP 200 within 5 seconds — offload business logic to a background job.
  5. 5Replay one failed event from the Lemon Squeezy dashboard and watch your logs.

Working Code

Copy-paste verified examples. Use the tab that matches your stack.

javascript
const crypto = require('crypto');

app.post('/webhooks/lemonsqueezy', express.raw({ type: 'application/json' }), (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');

  if (!crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expectedSig, 'hex'))) {
    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 });

  res.status(200).json({ received: true }); // respond first
  await processLemonEvent(eventName, event); // then process
});

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.