Signature verification
Every webhook request arrives with:
X-Infi-Timestamp: unix timestamp in seconds.X-Infi-Signature:sha256=<hex>, where<hex>is the HMAC-SHA256 computed over the concatenation"{timestamp}.{raw body}"using the webhook secret.
Algorithm
- Capture the raw body of the request (bytes, before any JSON parsing).
- Capture the
X-Infi-TimestampandX-Infi-Signatureheaders. - Concatenate:
payload = "{timestamp}.{rawBody}". - Compute
expected = HMAC-SHA256(WEBHOOK_SECRET, payload)and encode in hex. - Compare
"sha256=" + expectedwith the received header in constant time.
Use the raw body, not re-serialized JSON
The signature is over the exact bytes sent. Re-serializing the JSON on your side can change key order or spacing and break the verification. Configure your framework to preserve the raw body (e.g., in Express use express.raw({ type: 'application/json' })).
Where to get the `WEBHOOK_SECRET`
The secret is delivered together with your API key, through the secure channel agreed with INFI (encrypted email or dashboard). Do not expose it in public source code or logs — store as an environment variable.
Replay mitigation
Recommended: reject webhooks with X-Infi-Timestamp too old (e.g., > 5 minutes). This protects against replay of legitimate webhooks captured in transit.
Examples
# Shell validation — usually delegated to the application language.
# Concept:
# payload="${TIMESTAMP}.${RAW_BODY}"
# expected=$(echo -n "$payload" | openssl dgst -sha256 -hmac "$INFI_WEBHOOK_SECRET" | awk '{print $2}')
# [[ "sha256=$expected" == "$X_INFI_SIGNATURE" ]]