Documentation Index
Fetch the complete documentation index at: https://mintfax.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
Webhook signing and verification
Outcome
After following these steps you will have a webhook receiver that verifies HMAC-SHA256 signatures, rejects replayed requests, and captures the raw body correctly (the step most people get wrong). Code samples cover Node.js, Python, PHP, .NET, and Go.Prerequisites
- A mintfax account with a webhook endpoint configured. Your endpoint’s signing secret is shown once when you create or rotate it.
- Your signing secret stored as an environment variable. The examples below use
MINTFAX_WEBHOOK_SECRET. In the sandbox, this looks likewhsec_test_3JzE9rYNm2VbQ8P6KxLf1WdGa4Tc. - A publicly reachable HTTPS URL that can receive POST requests.
How mintfax signs webhooks
Every webhook request includes two headers:| Header | Value |
|---|---|
X-Mintfax-Signature | Hex-encoded HMAC-SHA256 of {timestamp}.{raw_body} |
X-Mintfax-Timestamp | Unix timestamp (seconds) when mintfax generated the payload |
., and the raw request body concatenated together. Including the timestamp prevents replay attacks because the signature is only valid for that specific moment and payload.
Step 1: Capture the raw request body
This is where most verification failures happen. If your framework parses JSON before your code runs, it may alter whitespace or key order. That changes the byte sequence and breaks the signature check.rawBody and confirm they match the original JSON, including whitespace.
Step 2: Reject stale timestamps
Compare theX-Mintfax-Timestamp header to the current time. Reject requests older than five minutes to block replay attacks.
Step 3: Verify the signature
Compute HMAC-SHA256 over{timestamp}.{rawBody} using your signing secret, then compare it to the X-Mintfax-Signature header. Use a constant-time comparison function. A naive == check leaks information through response timing, which an attacker can use to guess the correct signature byte by byte.
Step 4: Handle key rotation
When you rotate your signing secret (viaPOST /account/webhooks/{id}/rotate-secret), in-flight requests may still carry the old signature. To avoid dropping those:
- Store both the old and new secrets.
- Attempt verification with the new secret first.
- If that fails, try the old secret.
- After five minutes (the replay window), remove the old secret.
Verify
Send a test webhook from the sandbox. Use the sandbox signing secret (whsec_test_3JzE9rYNm2VbQ8P6KxLf1WdGa4Tc) and send a fax to +15005550001 (the success magic number). Your endpoint should:
- Accept the
fax.queuedevent with a200response. - Reject a replayed copy of the same request (timestamp too old or duplicate
event_id). - Reject a request with a modified body (signature mismatch).
event_id field in the payload for deduplication. Store each event_id you process and skip any you have already seen.
What to do next
- Events schema - full list of event types and payload shapes.
- Error catalog - machine-readable error codes and next actions.
- Idempotency keys - make retries safe on your outbound API calls.
- Rate limits - request limits and
Retry-Afterheaders. - Sandbox - magic numbers and simulated failure scenarios for integration testing.