Build a webhook receiver
A minimal, production-shaped endpoint that verifies WinLayer's signature and never blocks the sender.
1. Register the endpoint
On /developer, add your HTTPS URL. Copy the whsec_… secret (shown once) into your receiver's environment. Use the test ping button whenever you deploy a change.
2. Verify, then respond fast
The two rules of webhook receivers: compute the HMAC over the raw body (before JSON parsing), and return 2xx immediately — do real work asynchronously.
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const app = express();
const SECRET = process.env.WINLAYER_WEBHOOK_SECRET!;
// Capture the RAW body — signature verification needs the exact bytes.
app.post("/hooks/winlayer", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.header("x-winlayer-signature") ?? "";
const expected = "sha256=" + createHmac("sha256", SECRET).update(req.body).digest("hex");
const a = Buffer.from(sig);
const b = Buffer.from(expected);
if (a.length !== b.length || !timingSafeEqual(a, b)) {
return res.status(401).send("bad signature");
}
const { id, event, data } = JSON.parse(req.body.toString("utf8"));
if (await alreadyProcessed(id)) return res.sendStatus(200);
res.sendStatus(200); // ack first…
// …then persist id and act. e.g. event === "opportunity.pursuit.set"
// → upsert data.signalId with data.status into your system.
});
app.listen(8080);3. Know the delivery semantics
- Delivery is at least once. Network errors, timeouts, HTTP 408/425/429, and 5xx responses enter a durable five-attempt queue with 1, 5, 30, and 120 minute backoff.
- Events can arrive out of order under concurrency; treat
data.statusas the current truth, not a transition. - Make handling idempotent: store the stable payload
idand return 2xx without repeating work when that id arrives again. - The developer console shows pending retry count, next attempt, last HTTP status, and terminal failures. Test pings remain one-attempt diagnostics.
Event reference: payload shapes and headers are in Webhooks.