mockdock.dev

~/tools / webhook-emulator

Webhook Cryptographic Header Emulator

Webhook handlers reject anything without a valid signature — which makes local testing miserable without real provider secrets. Pick a provider, paste any secret and payload, and get the exact signature headers computed the way Stripe, GitHub, or any HMAC-SHA256 service computes them. Native crypto.subtle only: your secret never leaves this page.

sign request

— enter a secret to sign —

request headers
// signature headers will appear here
ready-to-fire curl
// curl command will appear here

$ man signature-schemes

  • stripe — HMAC-SHA256 over {timestamp}.{payload}, emitted as t={ts},v1={hex}. Timestamp is re-stamped on every edit.
  • github — HMAC-SHA256 over the raw body, emitted as sha256={hex}.
  • generic — plain hex HMAC-SHA256 of the raw body in x-signature.

Signatures are computed over the exact bytes in the textarea — whitespace included — matching how providers sign the raw request body.

cat ./tldr.txt

A webhook signature generator computes the cryptographic headers providers attach to their requests — Stripe stripe-signature, GitHub x-hub-signature-256, or generic HMAC-SHA256 — using the browser Web Crypto API, so signed requests can be sent to a local endpoint without provider credentials.

./when-to-use-this

  • Your handler verifies signatures and rejects every hand-crafted test request you send it.
  • You need to confirm your verification code rejects a tampered body as well as accepting a valid one.
  • You want a ready-to-paste cURL command that will actually pass signature verification locally.

./frequently-asked

How does Stripe signature verification actually work?
Stripe computes HMAC-SHA256 over the string "{timestamp}.{raw_body}" using your signing secret, then sends it as t={timestamp},v1={hex}. Your handler must recompute the same value over the raw, unparsed body and compare in constant time. Verifying against a re-serialised body is the most common cause of false rejections.
Is it safe to paste my real signing secret here?
The secret is used only inside your browser via crypto.subtle and is never sent anywhere — there is no server, no logging, and no network request. It is not written to the URL either. That said, for shared machines or recorded screens, a local test secret is always the more cautious choice.
Why does my signature not match even with the right secret?
Almost always whitespace. Signatures are computed over exact bytes, so a body that has been pretty-printed, re-serialised, or had a trailing newline added produces a completely different hash. Sign and verify the identical raw string, and make sure your framework is not parsing the body before verification runs.

ln -s ./related-tools