Signature Verification
MyMX signs every webhook request so you can verify it came from us. This prevents attackers from sending fake webhooks to your endpoint.
The signature header
Every webhook includes a MyMX-Signature header with this format:
t=1734523200,v1=5257a869e7ecebeda32aff1deadbeef951cad7e77a0e56ff536d0ce8e108d8bd
t- Unix timestamp when the signature was generatedv1- HMAC-SHA256 signature
Verifying with the SDK
The easiest way to verify signatures is with the official SDK:
import { verifyWebhookSignature, WebhookVerificationError } from 'mymx';try {verifyWebhookSignature({rawBody, // The raw request body as a stringsignatureHeader, // The MyMX-Signature header valuesecret: process.env.WEBHOOK_SECRET,});// Signature is valid} catch (err) {if (err instanceof WebhookVerificationError) {// Invalid signatureconsole.error('Verification failed:', err.code);}throw err;}
Manual verification
If you're not using the SDK, here's how to verify manually:
- Parse the header - Extract
t(timestamp) andv1(signature) - Check the timestamp - Reject if older than 5 minutes (prevents replay attacks)
- Compute expected signature - HMAC-SHA256 of
{timestamp}.{rawBody}using your secret - Compare signatures - Use constant-time comparison to prevent timing attacks
import hmacimport hashlibimport timedef verify_signature(raw_body, signature_header, secret):# Parse headerparts = dict(p.split('=', 1) for p in signature_header.split(','))timestamp = int(parts['t'])signature = parts['v1']# Check timestamp (5 min tolerance)if abs(time.time() - timestamp) > 300:return False# Compute expected signaturesigned_payload = f"{timestamp}.{raw_body}"expected = hmac.new(secret.encode(),signed_payload.encode(),hashlib.sha256).hexdigest()# Constant-time comparisonreturn hmac.compare_digest(signature, expected)
Finding your webhook secret
Your webhook secret is in the Webhooks section of your dashboard. Your webhook secret is global across all endpoints. Store it securely as an environment variable - never commit it to source control.
Rotating your secret
You can rotate your webhook secret from the dashboard at any time. When you rotate, the old secret is immediately invalidated. Make sure to update your webhook handlers before rotating to avoid verification failures.
Error codes
The SDK throws WebhookVerificationError with these codes:
| Code | Description |
|---|---|
INVALID_SIGNATURE_HEADER | Missing or malformed MyMX-Signature header |
TIMESTAMP_OUT_OF_RANGE | Timestamp is too old (possible replay attack) |
SIGNATURE_MISMATCH | Signature doesn't match expected value |
MISSING_SECRET | No webhook secret was provided |