Beam

Webhooks

Webhook signing

Verify that events really came from Beam using the Beam-Signature header.

The scheme

  • Header: Beam-Signature: t=<unix_seconds>,v1=<hex signature>
  • Signed payload: t + "." + raw_body (the exact raw bytes, never re-serialized JSON)
  • Algorithm: HMAC-SHA256 with your signing secret (Settings → Event webhooks)
  • Reject events older than 5 minutes to block replays, and compare with a constant-time function
import crypto from "node:crypto";

function verifyBeamSignature(rawBody, header, secret) {
  const m = /t=(\d+),v1=([a-f0-9]+)/.exec(header ?? "");
  if (!m) return false;
  const [, t, v1] = m;
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = crypto.createHmac("sha256", secret)
    .update(`${t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
Use the raw bodyVerify against the exact bytes you received. Parsing the JSON and re-serializing it will produce different bytes and a failed signature, even for a genuine event.