Orders or subscription updates happen, but your app state does not change.
Copy-paste verified examples. Use the tab that matches your stack.
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');
sectionTitles: {problem: 'Why Is Your Lemon Squeezy Webhook Not Working?', why: 'Root Causes of Lemon Squeezy Webhook Failures', fix: 'How to Fix Lemon Squeezy Webhook Signature Validation', code: 'Working Lemon Squeezy Webhook Handler Code', mistakes: 'Most Common Lemon Squeezy Webhook Mistakes', workflow: 'Lemon Squeezy Webhook Debugging Workflow', prevention: 'How to Prevent Lemon Squeezy Webhook Errors'},
},
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
});Provider delivery logs → app ingress logs → signature check → billing state update → replay verification.
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 testerWas this page helpful?
Your feedback helps us improve the docs.