Verify Speakvora webhook signatures
Validate webhook authenticity using HMAC-SHA256 signature verification with timestamp and raw body. Reject requests older than 5 minutes.
x-speakvora-signature header containing an HMAC-SHA256 hash of the timestamp and raw body. Verify the signature matches and reject requests older than 5 minutes to ensure authenticity and prevent replay attacks.Webhook signature headers
Each webhook request includes two security headers. The x-speakvora-timestamp header contains the timestamp when the webhook was sent. The x-speakvora-signature header contains the HMAC-SHA256 signature as a hexadecimal string.
- x-speakvora-timestamp: timestamp of the webhook send time
- x-speakvora-signature: hex-encoded HMAC-SHA256 signature
Signature computation
The signature is computed over the concatenation of the timestamp, a dot separator, and the raw HTTP request body. Use your webhook secret key (shown once in the portal) as the HMAC key. Always use the raw body bytes, not a parsed or re-serialized JSON string, to avoid whitespace or encoding mismatches.
Python example
import hmac
import hashlib
import time
def verify_webhook(request_body, timestamp_header, signature_header, secret):
# Reject if older than 5 minutes
current_time = int(time.time())
request_time = int(timestamp_header)
if current_time - request_time > 300:
return False
# Compute expected signature
message = f"{timestamp_header}.{request_body}".encode()
expected_sig = hmac.new(
secret.encode(),
message,
hashlib.sha256
).hexdigest()
# Compare signatures
return hmac.compare_digest(expected_sig, signature_header)Timestamp validation
Always check the timestamp before verifying the signature. Reject any webhook with a timestamp older than 5 minutes to prevent replay attacks. This protects against an attacker resending an old webhook request.
Webhook events
Speakvora currently sends three types of webhook events. Each event has a type field in the JSON body.
- test.ping: Test event sent when you create or update a webhook endpoint
- key.created: Sent when a new API key is generated
- key.revoked: Sent when an API key is revoked
Best practices
- Use a constant-time comparison function (like Python's hmac.compare_digest) to avoid timing attacks
- Store your webhook secret securely; never commit it to version control
- Reject requests older than 5 minutes to prevent replay attacks
- Always use the raw request body, not a re-parsed JSON object
- Log failed signature verifications for security monitoring
Frequently asked questions
What is the webhook secret?
The webhook secret is a unique key shown once in the portal when you create or view a webhook endpoint. Use it as the HMAC key for signature verification. Treat it like an API key and keep it secure.
Why should I reject old timestamps?
Rejecting requests older than 5 minutes prevents replay attacks, where an attacker resends an old webhook to trigger unintended actions. The 5-minute window allows for reasonable clock skew between servers.
What if the signature doesn't match?
If the signature verification fails, reject the webhook request and log the failure. Do not process the webhook. This indicates either a tampering attempt or a configuration error.
Can I use the parsed JSON body for the signature?
No. Always use the raw HTTP request body bytes. Parsing and re-serializing JSON can change whitespace or field order, causing the signature to mismatch even if the data is identical.
Which events does Speakvora send?
Currently, test.ping (for testing), key.created (new API key), and key.revoked (revoked API key). More event types may be added in the future.
Related: API documentation, pricing, developer guides and more answers.