Skip to main content

Webhook Security

Every outbound delivery is signed. Verify the signature before you parse, trust, or act on any payload.

The signature header

The value after sha256= is the hex-encoded HMAC-SHA256 of the exact raw request body, keyed with your endpoint’s signing secret.
The signing secret is issued by a VitaRelay admin in the dashboard (org profile → Webhooks panel) and is displayed once at creation. Store it in your secret manager. If it’s lost, ask an admin to rotate it.

Verify in Node.js

Compute the HMAC over the raw body bytes exactly as received. Re-serializing a parsed object (JSON.stringify(req.body)) changes whitespace and key order and will fail verification. In Express, use express.raw({ type: "application/json" }) for the webhook route.

Rules

  • Reject with 401 if X-VitaRelay-Signature is missing, malformed, or does not match.
  • Always use a constant-time comparison (crypto.timingSafeEqual) — never ===.
  • Never log the signing secret or the full signature header.
  • Terminate webhooks over HTTPS only.

Replay protection

Each delivery carries X-VitaRelay-Timestamp (unix seconds) and X-VitaRelay-Delivery (a UUID). To limit replay exposure:
Pair that with deduplication on X-VitaRelay-Delivery so a replayed or retried delivery is a no-op.
Deliveries are at-least-once. A rejected-then-retried delivery arrives with the same delivery id, so idempotent handling is required, not optional.