Securing Webhooks (HMAC)
Understand how the webhooks are secured at each end point.
Overview
Every webhook Begini sends includes an X-Signature header containing an HMAC SHA-512 hash of the raw request body, generated using your API key as the secret. You must verify this signature before processing any payload.
Skipping verification means your endpoint will accept forged or tampered requests.
How it works
- Begini generates the JSON payload
- Begini hashes the raw payload body using your API key with HMAC SHA-512
- The hash is sent in the
X-Signaturerequest header - Your server receives the request, reads the raw body, and generates the same hash independently
- You compare your hash against the
X-Signaturevalue — if they match, the request is genuine
Request headers
X-Signature: <hmac_sha512_hexdigest>
Content-Type: application/json
Implementation
Critical: use the raw request body
You must hash the raw request body exactly as received — before any JSON parsing. Any modification (reformatting, whitespace changes, re-serialisation) will produce a different hash and verification will fail.
Python (FastAPI)
import hashlib
import hmac
from fastapi import APIRouter, Request, HTTPException, status
router = APIRouter()
@router.post('/webhook-receiver')
async def receive_webhook(request: Request):
payload = await request.body()
signature_from_request = request.headers.get("X-Signature")
expected_signature = hmac.new(
'<YOUR_API_KEY>'.encode('utf-8'),
payload,
digestmod=hashlib.sha512
).hexdigest()
if not hmac.compare_digest(expected_signature, signature_from_request):
raise HTTPException(status_code=403, detail='Validation Failure')
return status.HTTP_200_OK
Python (manual verification)
import hmac
import hashlib
def verify_webhook(raw_body: bytes, received_signature: str, api_key: str) -> bool:
expected = hmac.new(
api_key.encode('utf-8'),
raw_body,
digestmod=hashlib.sha512
).hexdigest()
return hmac.compare_digest(expected, received_signature)
Always use
hmac.compare_digestfor the comparison — this performs a constant-time comparison and prevents timing attacks. Do not use==.
Common mistakes
| Mistake | Effect |
|---|---|
| Parsing JSON before hashing | Different hash, verification always fails |
| Using the wrong API key (Test vs Production) | Verification fails in one environment |
Using == instead of compare_digest
|
Timing attack vulnerability |
| Ignoring verification failures | Forged requests processed |
If verification fails
Return a non-200 response (e.g. HTTP 403), do not process the payload, and log the failure for investigation. Never silently accept a request that fails verification.
Additional security measures
HMAC verification should be combined with:
- HTTPS-only endpoints
- IP whitelisting (contact your account manager for Begini's IP ranges)
- Rate limiting on your receiver endpoint
- Secure storage of your API key
Next steps
- Testing Webhooks — validate your endpoint using the test API
- Webhook Troubleshooting — diagnose delivery and processing issues
- Security Checklist — full pre-launch security checklist
Was this article helpful?
Give feedback