Webhooks
Get notified the moment a waiver is signed. Add an endpoint from your dashboard under Settings → Integrations (admins only). We'll POST a JSON payload there for every matching event.
Events
The only event available today. More may be added later.
| Event | Description |
|---|---|
| waiver.signed | A waiver was signed and stored. |
Payload
Every event is a POST request with a JSON body shaped like this. data varies by event type; this is waiver.signed's shape.
{
"type": "waiver.signed",
"data": {
"signatureId": "77e81188-0bb5-452b-b902-8fc49fbbdefb",
"waiverId": "d672eef4-24f2-4316-b6c7-c841ff179e74",
"waiverTitle": "Guest Release Waiver",
"customerName": "Jordan Reyes",
"customerEmail": "jordan@example.com",
"customerPhone": "555-0142",
"submittedAt": "2026-08-24T18:04:00.191Z"
}
}Verifying the signature
Every request includes an X-Superwaiver-Signature header: the hex-encoded HMAC-SHA256 of the raw request body, signed with your webhook's secret (shown when you added it, and again anytime from its row in Settings → Integrations). Verify it against the raw body before parsing, not the re-serialized JSON, which may not byte-for-byte match what was signed.
Example
const crypto = require("crypto")
function isValidSignature(rawBody, signatureHeader, secret) {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex")
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader))
}Example receiver
A minimal endpoint that verifies the signature and reads the event. Respond with any 2xx status to mark a delivery as successful; anything else (or a timeout) is recorded as failed.
Example
const http = require("http")
const crypto = require("crypto")
const WEBHOOK_SECRET = "whsec_..." // from Settings -> Integrations
function isValidSignature(rawBody, signatureHeader, secret) {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex")
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader))
}
http
.createServer((req, res) => {
if (req.method !== "POST") {
res.writeHead(405).end("Method not allowed")
return
}
let rawBody = ""
req.on("data", (chunk) => (rawBody += chunk))
req.on("end", () => {
const signatureHeader = req.headers["x-superwaiver-signature"] || ""
if (!isValidSignature(rawBody, signatureHeader, WEBHOOK_SECRET)) {
res.writeHead(401).end("Invalid signature")
return
}
const payload = JSON.parse(rawBody)
if (payload.type === "waiver.signed") {
// ...do something with payload.data.waiverTitle, payload.data.customerEmail, etc.
}
res.writeHead(200).end("OK")
})
})
.listen(3000)Delivery
Each webhook's row in Settings → Integrations shows when it last fired and whether that delivery succeeded. A failed delivery isn't retried automatically: if your endpoint was down, you'll need to catch up from your own records (e.g. the search API) rather than waiting for a redelivery.