Skip to main content

Why Webhook Security Matters

Without signature verification, anyone who discovers your webhook URL could send fake events to your application, potentially causing:
  • ❌ Fake completion events triggering incorrect business logic
  • ❌ Data corruption from processing invalid events
  • ❌ Unauthorized access to your application
  • ❌ Security vulnerabilities
✅ Solution: Verify that every webhook is actually from Magic Hour using cryptographic signatures.
Production Requirement: Always verify webhook signatures in production. This is critical for security.

How Signature Verification Works

Magic Hour signs every webhook using HMAC-SHA256 with your webhook secret:
  1. Magic Hour creates a signed payload: {timestamp}.{json_payload}
  2. Magic Hour computes HMAC-SHA256 signature using your webhook secret
  3. Magic Hour sends the webhook with signature and timestamp in headers
  4. Your app recreates the same signed payload
  5. Your app computes the same HMAC-SHA256 signature
  6. Your app compares signatures - if they match, the webhook is authentic

Security Headers

Every webhook includes two security headers to help you verify authenticity:

magic-hour-event-signature

The HMAC-SHA256 signature computed using your webhook secret and the signed payload. Example:
What it is: A 64-character hexadecimal string representing the HMAC-SHA256 hash.

magic-hour-event-timestamp

Unix timestamp (seconds since epoch) when Magic Hour sent the webhook. Example:
What it’s for:
  • Prevents replay attacks by rejecting old webhooks
  • Recommended tolerance: Accept webhooks within 5 minutes of current time
  • If timestamp is too old or too far in the future, reject the webhook
Header Case: HTTP headers are case-insensitive. Magic-Hour-Event-Signature and magic-hour-event-signature are equivalent.
Optional but Recommended: Signature verification is not required to receive webhooks, but it’s strongly recommended for production to ensure authenticity.
1

Extract Headers and Payload

Get the signature, timestamp, and raw JSON payload from the incoming request:
2

Extract the headers and payload

What to do: Get three pieces of information from the incoming webhook request:
  1. Signature header: magic-hour-event-signature
  2. Timestamp header: magic-hour-event-timestamp
  3. Raw JSON body: The entire request body as a string (not parsed yet)
Code example:
Critical: You must use the raw request body exactly as received. Do NOT parse the JSON and re-stringify it, as this can change formatting and break signature verification.
3

Create the signed payload

What to do: Combine the timestamp and raw payload into a single string for signature computation.Format: {timestamp}.{raw_json_payload}Example signed payload:
Code example:
Why: The signed payload is what Magic Hour used to create the signature. You need to recreate it exactly the same way.
4

Compute your signature

What to do: Generate an HMAC-SHA256 hash using:
  • Key: Your webhook secret (from Magic Hour Developer Hub)
  • Message: The signed payload you just created
Algorithm: HMAC-SHA256 (Hash-based Message Authentication Code with SHA-256)
5

Verify the signature

What to do: Compare your computed signature with the signature Magic Hour sent in the header.Important: Use a constant-time comparison to prevent timing attacks:
Why constant-time comparison: Regular string comparison (==) can leak timing information that attackers could exploit. hmac.compare_digest() prevents this.
6

Verify the timestamp

What to do: Check that the webhook was sent recently (within 5 minutes is recommended).Why: Prevents replay attacks where someone could resend an old valid webhook.
Adjust tolerance: You can adjust the 300-second (5-minute) window based on your needs, but keep it reasonable for security.

Complete Secure Handler Implementation

Testing Your Secure Handler

Test signature verification with a sample webhook:

Best Practices

1. Environment Variables

Store your webhook secret securely:

2. Error Handling

Handle verification failures gracefully:

3. Logging

Log security events for monitoring:

4. Rate Limiting

Protect against abuse:

Troubleshooting

Common Issues

“Invalid signature” errors:
  • ✅ Ensure you’re using the raw JSON payload, not parsed/re-stringified
  • ✅ Check that your webhook secret is correct
  • ✅ Verify timestamp format (Unix seconds, not milliseconds)
  • ✅ Make sure you’re concatenating timestamp.payload correctly
“Timestamp too old” errors:
  • ✅ Check server clock synchronization
  • ✅ Increase tolerance window if needed (but keep it reasonable)
  • ✅ Ensure timestamp is in seconds, not milliseconds
Missing headers:
  • ✅ Verify webhook is registered correctly in Magic Hour
  • ✅ Check that your endpoint URL is correct
  • ✅ Ensure headers are being received (log all headers for debugging)

Debug Mode

Add debug logging to troubleshoot signature issues:

Next Steps

Event Types Reference

Learn about all available webhook events and their payloads

Production Deployment

Deploy secure webhook handlers to production

Webhook Quickstart

Start with basic webhook setup and testing

API Reference

Complete webhook API documentation
Need help with webhook security? Contact support@magichour.ai