Docs

Verify signatures

Check that a request really came from Cueston and wasn't replayed.

Every request has a Cueston-Signature header:

Text
Cueston-Signature: t=1793021160,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  • t is the Unix time the request was signed.
  • v1 is the hex HMAC-SHA256 of t + "." + rawBody, keyed with your endpoint's secret (the whole whsec_… string, as UTF-8).

To verify:

  1. Take the body exactly as received, before parsing JSON.
  2. Compute the HMAC of `${t}.${rawBody}` with your secret.
  3. Compare it, in constant time, with each v1 value. One match is enough.
  4. Reject timestamps more than 5 minutes from your clock, so an old request can't be replayed.
JavaScript
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the request body exactly as received (a string or Buffer, before JSON parsing).
export function verifyCuestonSignature(header, rawBody, secret = process.env.CUESTON_WEBHOOK_SECRET) {
  const items = header.split(",").map((part) => part.split("="));
  const t = Number(items.find(([key]) => key === "t")?.[1]);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // reject replays older than 5 minutes
  const expected = Buffer.from(createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"));
  return items.some(([key, value]) => key === "v1" && value?.length === 64 && timingSafeEqual(Buffer.from(value), expected));
}

Rotating the secret

Rotate an endpoint's secret in Settings or with POST /api/webhooks/{id}/rotate-secret. The new secret is shown once. For the next 24 hours each request carries two v1 signatures, one with the new secret and one with the old, so your server verifies with either while you deploy the new one. Pass { "expirePrevious": true } to stop signing with the old secret right away.

On this page