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');
}
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.