Example receiver
A complete Express handler that verifies the signature, deduplicates by event ID, and routes to per-event handlers.
import express from 'express';
import crypto from 'node:crypto';
const app = express();
const SECRET = process.env.KEY_WEBHOOK_SECRET;
const seen = new Set(); // in prod, use Redis / a TTL store
function verify(rawBody, signature) {
const expected =
'sha256=' +
crypto.createHmac('sha256', SECRET).update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
app.post(
'/webhooks/key',
express.raw({ type: 'application/json' }),
(req, res) => {
const sig = req.headers['x-webhook-signature'];
const eventId = req.headers['x-event-id'];
if (!sig || !verify(req.body, sig)) return res.sendStatus(401);
if (seen.has(eventId)) return res.sendStatus(200); // dedup
seen.add(eventId);
const event = JSON.parse(req.body);
switch (event.eventType) {
case 'member.joined': onJoined(event); break;
case 'member.approved': onApproved(event); break;
case 'member.rejected': onRejected(event); break;
case 'member.removed': onRemoved(event); break;
case 'member.left': onLeft(event); break;
case 'webhook.test': /* no-op */ break;
}
res.sendStatus(200);
}
);
app.listen(3000); ← In the product, this is also reachable from Community Settings → Developer → Webhooks.