X-OpenFX-Signature header containing an
HMAC-SHA256 signature computed over the raw request body. Verifying this signature is the
only way to confirm that the webhook was genuinely sent by OpenFX and was not altered
in transit.
Every delivery also includes an X-OpenFX-Event-Id header containing the event ID (evt_)
associated with the delivery. Use this for deduplication and cross-referencing with the
Events API (GET /events).
How it works
- When you create a webhook subscription, OpenFX generates a unique signing secret and returns it in the response. This secret is shared between OpenFX and your application.
- For each delivery, OpenFX computes
HMAC-SHA256(signing_secret, raw_request_body)and includes the hex-encoded result in theX-OpenFX-Signatureheader. - Your endpoint reads the raw request body, computes the same HMAC using the stored signing secret, and compares the result against the header value.
- If the values match, the payload is authentic. If not, reject it.
Step-by-step verification
Step 1: Extract the signature header
Read theX-OpenFX-Signature header from the incoming request. This is a hex-encoded
string.
Step 2: Get the raw request body
Read the raw request body as bytes. Do not parse it to JSON first, because JSON serialization may change whitespace or field ordering, which would invalidate the signature.Step 3: Compute the expected signature
Use your stored signing secret and the raw body to compute an HMAC-SHA256 digest. Hex-encode the result.Step 4: Compare signatures
Compare your computed signature against theX-OpenFX-Signature header value. Use a
constant-time comparison function to prevent timing attacks.
Step 5: Accept or reject
If the signatures match, the webhook is authentic — process it. If they do not match, return a401 or 403 status code and do not process the payload.
Code examples
Replay protection
Every webhook delivery includes anX-OpenFX-Timestamp header containing the Unix timestamp (seconds) when the delivery was sent. To prevent replay attacks, reject any delivery where the timestamp is more than 300 seconds (5 minutes) from the current time.
For maximum security, include the timestamp in your signature verification by
verifying both
X-OpenFX-Signature and X-OpenFX-Timestamp together. The
server-side signature computation includes the timestamp, so a tampered
timestamp will cause signature verification to fail.Common pitfalls
Parsing the body before verifying
If your web framework automatically parses the JSON body and you re-serialize it for verification, the result may differ from the original payload due to whitespace or field ordering changes. Always verify against the raw bytes of the request body.In Express.js, use
express.raw({ type: "application/json" }) on your webhook route
instead of express.json(). In Flask, use request.get_data() instead of
request.get_json() for the verification step. In Go, use io.ReadAll(r.Body).Not using constant-time comparison
A naive string comparison (==) can leak timing information that allows an attacker to
determine the correct signature byte by byte. Always use a constant-time comparison
function:
| Language | Function |
|---|---|
| Python | hmac.compare_digest(a, b) |
| JavaScript | crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b)) |
| Go | hmac.Equal(a, b) |
| Ruby | Rack::Utils.secure_compare(a, b) |
| Java | MessageDigest.isEqual(a, b) |
Mismatched secret
If verification consistently fails, check that:- You are using the signing secret from the correct subscription (you may have multiple).
- The secret has not been rotated. If it was, use the new secret. During the 24-hour grace period after rotation, try both the old and new secrets.
- The secret is stored correctly — no trailing whitespace or encoding issues.
What to do when verification fails
If the signature does not match:- Return a
401or403status code. Do not process the payload. - Log the failure with the received signature, your computed signature, and the request headers for debugging.
- Do not expose details in the HTTP response body. A simple “Unauthorized” is sufficient.
- Alert on repeated failures. Consistent verification failures may indicate an attack, a misconfigured secret, or a secret rotation you missed.