Every delivery carries an X-Inboundr-Signature header. Verify it with your endpoint’s secret (the whsec_… value shown once when you created the endpoint) before trusting the payload.
Deliveries also still carry the original X-Inbound-Signature (no r) with an identical value, so handlers written before the rename keep working. Read X-Inboundr-Signature in new code — or use the inboundr npm package, whose readSignatureHeader accepts either.

Signature format

  • t — the Unix timestamp when the signature was generated.
  • v1HMAC-SHA256(secret, "{t}.{rawBody}"), hex-encoded.
The signed message is the timestamp, a literal ., then the raw request body. Compute the HMAC over the exact bytes you received — don’t re-serialize the JSON.

Verify it

Use a constant-time comparison (timingSafeEqual / compare_digest / hash_equals), not ==, to avoid timing attacks.

Reject stale deliveries

To defend against replay, reject deliveries whose timestamp t is too far from now (for example, more than 5 minutes):

Reading the raw body

Frameworks that auto-parse JSON can change the bytes. Read the raw body first:
  • Expressexpress.raw({ type: "application/json" }), then JSON.parse after verifying.
  • Next.js route handlersawait req.text(), verify, then JSON.parse.
  • Fastify — enable rawBody and hash that.
  • PHP — read file_get_contents("php://input"), verify, then json_decode.