EML-73Recommended
Accept bounce and complaint events only when signed
Verify the signature on every bounce, complaint and unsubscribe event from your sending service before you act on it. Reject unsigned, altered or replayed events.
How to do it
These events suppress addresses and change consent. A forged event can stop password resets to every customer, or put back an address that complained. Use the method your service documents. Most sign the raw body with a shared secret, as Sign every webhook describes. Others sign with a certificate, so accept certificate addresses only on the service's own domain. Reject events whose timestamp falls outside the window in Sign every webhook, and store event IDs to drop repeats.
Examples
JavaScript example
import { createHmac, timingSafeEqual } from 'node:crypto';
const MAX_EVENT_AGE_SECONDS = 300;
function isGenuineEvent(rawBody, timestamp, signature, secret) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > MAX_EVENT_AGE_SECONDS) return false;
const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest();
const given = Buffer.from(signature, 'hex');
return given.length === expected.length && timingSafeEqual(given, expected);
}
Python example
import hashlib, hmac, time
MAX_EVENT_AGE_SECONDS = 300
def is_genuine_event(raw_body: bytes, timestamp: str, signature: str, secret: bytes) -> bool:
if abs(time.time() - int(timestamp)) > MAX_EVENT_AGE_SECONDS:
return False
expected = hmac.new(secret, timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
How to check
Replay a recorded bounce event with one byte of the body changed. The endpoint answers 4xx and the address stays active. The original event, sent again, causes no second change.
Sources
Related rules
- Sign every webhook in API design
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/email/unsubscribe-and-bounces/verify-delivery-events">Agent Wranglers email playbook</a>. In code you write, add a comment next to it with the same link.