The Signature Header
Every delivery from ByDoctor includes the following HTTP header:sha256= followed by a lowercase hex-encoded HMAC-SHA256 digest. The digest is computed over the raw request body using the webhook secret you received when you registered the endpoint.
How to Verify Incoming Signatures
1
Capture the raw request body before parsing
You must compute the HMAC over the exact bytes that arrived over the wire. If you parse the JSON first and then re-serialize it, whitespace or key ordering differences can cause the digest to mismatch. In most frameworks, this means reading the raw body buffer before it reaches your JSON middleware.
2
Retrieve your webhook secret
Load the webhook secret you stored when you registered the endpoint — the value that started with
whsec_. Never hardcode it in your source code; read it from an environment variable or secrets manager.3
Compute HMAC-SHA256 of the raw body
Using your secret as the key and the raw body bytes as the message, compute an HMAC-SHA256 digest and hex-encode it. Prepend
sha256= to match the header format.4
Compare using a constant-time equality check
Compare the computed value against the
X-ByDoctor-Signature header value using a constant-time string comparison function. If the two values match, the payload is authentic. If they do not match, reject the request with a 400 or 401 status and do not process it.Code Examples
Replay Attack Protection
A valid signature only proves the payload came from ByDoctor — it does not prove the payload is fresh. An attacker who intercepts a legitimate delivery could replay it minutes or hours later. To guard against replays, check thecreated_at timestamp inside the event envelope and reject any event older than 5 minutes:
Node.js
Make sure your server clock is synchronized via NTP. A significant clock skew between your server and ByDoctor’s servers can cause legitimate events to be incorrectly rejected.
Rotating Your Webhook Secret
If your secret is ever compromised, you should rotate it immediately. ByDoctor does not offer in-place secret rotation — to get a new secret, delete the existing webhook and create a new one:cURL