Flow Studio
Signature Verification
HMAC-SHA256 signature verification on inbound webhooks — computing the expected signature, validating the header, and rejection behavior.
The X-Webhook-Signature Header
Every inbound webhook request must include an X-Webhook-Signature header with an HMAC-SHA256 signature of the request body. The format is:
X-Webhook-Signature: sha256={hex-encoded-HMAC}
Computing the Signature (Sender Side)
The external system (e.g., Shopify, GitHub, your custom sender) must compute the signature using the shared secret:
# Python example for the sending system
import hmac, hashlib
secret = b"whs_sk_live_Xy9mQ3..." # your webhook secret
body = b'{"event":"order.created","orderId":"ord-001"}'
signature = hmac.new(secret, body, hashlib.sha256).hexdigest()
headers = {
"X-Webhook-Signature": f"sha256={signature}",
"Content-Type": "application/json"
}
// Node.js example
const crypto = require('crypto');
const secret = 'whs_sk_live_Xy9mQ3...';
const body = JSON.stringify({ event: 'order.created', orderId: 'ord-001' });
const sig = crypto.createHmac('sha256', secret).update(body).digest('hex');
// Header: X-Webhook-Signature: sha256={sig}
Verification (BizFirstAI Side)
// ProcessEngine/Webhooks/WebhookSignatureVerifier.cs
public class WebhookSignatureVerifier
{
public bool Verify(string rawBody, string signatureHeader, string secret)
{
if (!signatureHeader.StartsWith("sha256="))
return false;
var receivedSig = signatureHeader["sha256=".Length..];
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var expectedSig = Convert.ToHexString(
hmac.ComputeHash(Encoding.UTF8.GetBytes(rawBody))
).ToLowerInvariant();
// Constant-time comparison to prevent timing attacks
return CryptographicOperations.FixedTimeEquals(
Convert.FromHexString(receivedSig),
Convert.FromHexString(expectedSig)
);
}
}
Rejection Behavior
| Condition | HTTP Response | Workflow |
|---|---|---|
| Missing signature header | 401 Unauthorized | Not started |
| Invalid signature | 401 Unauthorized | Not started |
| Valid signature | 200 OK (immediate mode) | Started |
Use the raw body for verification. Parse the JSON AFTER verifying the signature, and always sign/verify the raw bytes — not the parsed/re-serialized JSON. Key ordering differences in serialization will cause signature mismatches.