If your Patreon webhook verifier is copy-pasted from a GitHub or Stripe integration,
it will never match — not because of a subtle byte issue, but because Patreon uses a
different hash. Patreon signs every webhook with
HMAC-MD5, hex-encoded, over the raw request body. No
sha256=-style prefix, no timestamp, no base64. Per docs.patreon.com
(verified 2026-08-30), X-Patreon-Signature is "the HEX digest of the
message body HMAC signed (with MD5) using your webhook's secret". Patreon is, as far as
I can find, the only major provider still signing with MD5. Here are the failure causes
in the order to check them.
POST /your-endpoint HTTP/1.1
Content-Type: application/json
X-Patreon-Event: members:pledge:create
X-Patreon-Signature: ce471d0a0b33ed30125fa25e1a4d6c8d
{ "data": { "type": "member", ... }, "included": [...], "links": {...} }
The signature is computed as:
signature = hex( HMAC_MD5( key = webhook_secret, message = raw_body ) )
Note the digest is 32 hex chars (128-bit MD5), not the 64 you'd get from SHA-256 — that length alone tells you which hash you're dealing with.
Every muscle-memory verifier — GitHub's sha256=, Stripe's
v1=, Shopify's base64 — is HMAC-SHA256. Swap the hash and everything else
about your pipeline can be correct while nothing ever matches:
# Node
crypto.createHmac('md5', secret).update(rawBody).digest('hex')
# Python
hmac.new(secret.encode(), raw_body, hashlib.md5).hexdigest()
# CLI sanity check
openssl dgst -md5 -hmac "$SECRET" < body.bin
This is the failure unique to Patreon. crypto.subtle — the WebCrypto
API used in browsers, Cloudflare Workers, and Deno-style runtimes —
does not implement MD5 in any form. importKey with
{ hash: 'MD5' } throws. So WebCrypto-based verifier snippets (and most
online HMAC testers, which are built on WebCrypto) fail before they compute anything.
Your options on those runtimes are a pure-JS MD5 implementation (RFC 1321 is ~100 lines)
or verifying somewhere Node's crypto is available. This site's
signature debugger and capture bins ship a
pure-JS RFC-1321 HMAC-MD5 precisely for this — the debugger stays 100% client-side.
The classic, and it bites harder here because Patreon's JSON:API payloads are large
and deeply nested: the HMAC covers the raw request bytes, so any
framework that parses and re-stringifies JSON before your verifier runs (key reordering,
whitespace, +00:00 timezone formats in timestamps) produces a different
digest. Verify against the raw body, then parse.
Patreon's webhook secret is not show-once (unlike Stripe, Mux or
Frame.io): you can read it any time on the
Platform portal webhooks
page, or as the secret field on API webhook objects
(GET /api/oauth2/v2/webhooks). So "I lost the secret" is never the issue —
but each webhook has its own secret, so if you registered one webhook
via the portal and another via the API, verifying portal deliveries with the API
webhook's secret fails every time.
A near-miss that looks like corrupt payloads: one Patreon webhook can subscribe to
several triggers (members:create/update/delete,
members:pledge:create/update/delete, posts:publish/update/delete)
and every members:* delivery is the same
type: "member" JSON:API document. The X-Patreon-Event header is
the only reliable discriminator. Also: members:pledge:create fires when an
existing follower upgrades to patron, and members:create can fire more than
once per person (delete + renew) — dedupe on the member UUID (data.id).
And if your payload has type: "pledge" with integer IDs, you're reading an
old APIv1 tutorial — those pledge:* triggers are deprecated.
Point your Patreon webhook at a Hookden bin, set the bin's signature scheme to Patreon and paste your secret — every capture gets a live ✓/✗ badge computed from the exact bytes Patreon sent. If the badge is ✓ but your handler rejects the same delivery, the bug is your body handling, not your key. Or poke at the members:pledge:create payload example — its signature actually verifies against the documented sample secret.
paused: true —
deliveries stop entirely until you PATCH it back with
paused: "false" (which also flushes the queue) or press the portal's
"send queued messages" button. If Patreon events stopped arriving, check
paused and num_consecutive_times_failed on the webhook object
first.data.id + X-Patreon-Event.Related: retry-schedule comparison across 23 providers, Stripe signature failures, and webhooks to localhost for developing your handler behind the bin.
No signup needed. Or from your terminal: curl https://hookden.pages.dev/new
← All guides · Signature debugger · Payload examples · Docs · Hookden vs webhook.site