Verify a Mittr signature
Every webhook Mittr sends carries an HMAC signature so your receiver can prove the request came from us and wasn’t tampered with on the way. If you’re seeing signature mismatches, paste the headers + body + secret below — this page tells you whether they verify and, if not, the most likely reason why.
Everything runs in your browser. The secret, payload, and signature never leave this page. There is no network call, no logging, no analytics on this form.
How Mittr signs your webhooks
Section titled “How Mittr signs your webhooks”Every outbound delivery includes three headers:
| Header | Example | What it means |
|---|---|---|
X-Mittr-Signature | v1=8a1b...c3 (64-char lowercase hex, prefixed with v1=) | HMAC-SHA256(<secret>, "<ts>.<body>") |
X-Mittr-Timestamp | 1706123456 | Unix seconds when we signed the delivery |
X-Mittr-Event-ID | evt_… | The event ID. Useful for idempotency. |
The signed message is the timestamp, a literal dot, and the raw request body. Not the headers. Not the URL. Not a re-formatted body. The exact bytes your endpoint received.
Verify on your own server
Section titled “Verify on your own server”Always-on verification in three lines per language. These mirror exactly what the form above does.
import crypto from 'node:crypto';
export function verify(secret, signatureHeader, timestampHeader, rawBody) { const expected = 'v1=' + crypto .createHmac('sha256', secret) .update(`${timestampHeader}.${rawBody}`) .digest('hex'); return crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));}Python
Section titled “Python”import hmac, hashlib
def verify(secret: str, signature_header: str, timestamp_header: str, raw_body: bytes) -> bool: msg = f"{timestamp_header}.".encode() + raw_body expected = "v1=" + hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest() return hmac.compare_digest(signature_header, expected)import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt")
func Verify(secret, signatureHeader, timestampHeader string, rawBody []byte) bool { mac := hmac.New(sha256.New, []byte(secret)) fmt.Fprintf(mac, "%s.", timestampHeader) mac.Write(rawBody) expected := "v1=" + hex.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(signatureHeader), []byte(expected))}Replay protection
Section titled “Replay protection”The default replay window is 5 minutes, with a 1-minute clock-skew allowance for receivers ahead of us. If your handler reads the timestamp, reject anything older than 5 minutes even when the signature matches — that’s how you stop someone replaying a captured delivery later.