Securing Webhooks
Truedy signs every webhook request so you can confirm:- The request really came from Truedy
- The payload wasn’t modified in transit
- The request isn’t a replay of an older delivery
Headers sent with every request
| Header | Value |
|---|---|
X-Truedy-Timestamp | Unix timestamp in seconds (when the event was dispatched) |
X-Truedy-Signature | HMAC-SHA256 hex digest of the signed message |
Content-Type | application/json |
Signature algorithm (step by step)
- Read the raw request body bytes exactly as received — do not parse JSON first.
- Extract
X-Truedy-TimestampandX-Truedy-Signature. - Build the signed message:
"{timestamp}.{raw_body_as_string}" - Compute
HMAC-SHA256(key=webhook_secret, message=signed_message)and hex-encode it. - Compare with
X-Truedy-Signatureusing a constant-time comparison. - Reject if the timestamp is more than 5 minutes old (replay protection).
Implementation examples
Where to find your webhook secret
- Open the Truedy dashboard → Settings → Webhooks
- Select (or create) your webhook endpoint
- Copy the Signing Secret — it looks like
whsec_...
TRUEDY_WEBHOOK_SECRET). Never hardcode it in source files.
Security checklist
Always use HTTPS — never accept webhooks over plain HTTP in production
Read the raw body bytes before parsing JSON — parsers may normalize whitespace and break HMAC
Reject stale timestamps (>5 min old) to block replay attacks
Use constant-time comparison (
timingSafeEqual / compare_digest) — never === on signaturesReturn
2xx immediately after verification — process asynchronously in a background jobStore your webhook secret in an environment variable, not in source code
Treat all payload fields as untrusted input — validate types and handle missing fields
Troubleshooting signature failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Signature always wrong | Parsing JSON before reading raw bytes | Use express.raw() / request.get_data() before parsing |
| Works locally, fails in prod | Body modified by middleware/proxy | Ensure raw body passthrough; check load balancer config |
| Intermittent failures | Clock drift between servers | Sync server time via NTP; widen timestamp window slightly |
| Timestamp rejected | Slow processing between receipt and verification | Move verification to the very first line of your handler |
Next steps
Available Webhooks
Full event catalogue and payload shapes
Error Handling & Retries
Idempotent receiver runbook

