Mailchimp · event: subscribe (audience webhook) ·
content type: application/x-www-form-urlencoded
This is a representative sample — fake IDs, real structure. To see a payload with your data, point the provider at a capture URL (button below).
One click creates a free capture bin with this exact payload already in it — pretty-printed, headers inspectable, replayable to your own endpoint:
Then paste the bin's URL into Mailchimp's webhook settings to capture a real delivery. No signup.
content-type: application/x-www-form-urlencoded x-mailchimp-signature: t=1788520917,v1=d2d335a2097f8f3738b75df50aed7dc8e6210ad39bb8bcf74c607c571625ee31
type=subscribe& fired_at=2026-08-30+11%3A21%3A57& data%5Bid%5D=8a25ff1d98& data%5Blist_id%5D=a6b5da1054& data%5Bemail%5D=ada%40example.com& data%5Bemail_type%5D=html& data%5Bip_opt%5D=203.0.113.7& data%5Bip_signup%5D=203.0.113.7& data%5Bmerges%5D%5BEMAIL%5D=ada%40example.com& data%5Bmerges%5D%5BFNAME%5D=Ada& data%5Bmerges%5D%5BLNAME%5D=Lovelace& data%5Bmerges%5D%5BINTERESTS%5D=Newsletter%2CProduct+updates
application/x-www-form-urlencoded with PHP-style bracket keys
(data[merges][FNAME]). Mailchimp's own docs show the payloads "parsed as
JSON", which is how half the internet ends up writing a JSON handler that explodes on
the real thing. A capture bin decodes the fields for you and keeps the raw bytes.X-Mailchimp-Signature: t=<unix>,v1=<hex> — hex
HMAC-SHA256 over timestamp.rawBody, i.e. Stripe's exact wire
format under Mailchimp's header (same family as Calendly and Mux). Secret
here: hookden_mailchimp_sample_signing_secret — try the
signature debugger (Mailchimp provider) or set
the Mailchimp scheme on a capture bin for live ✓/✗ badges. Sign the
raw urlencoded bytes: URL-decoding or re-encoding the body first
changes the bytes and fails verification. Docs suggest rejecting timestamps older
than 5 minutes.signing_secret in the API create response). There
is no way to retrieve it later — lose it and you delete and recreate the
webhook (verified vs Mailchimp's developer guide, 2026-08-30).fired_at is
2026-08-30 11:21:57 — space-separated, no timezone (it's UTC), not
ISO-8601. merges.INTERESTS is a comma-joined string, not an array. The
unsubscribe event has a different shape (adds action,
reason, campaign_id) — branch on type
before touching fields. And a profile update can fire upemail
and profile for one edit — dedupe on
(type, fired_at, data[id]).Reproduce this delivery against any endpoint (your handler, a bin, staging):
$ curl -X POST https://your-endpoint.example/hook \ -H 'content-type: application/x-www-form-urlencoded' \ -H 'x-mailchimp-signature: t=1788520917,v1=d2d335a2097f8f3738b75df50aed7dc8e6210ad39bb8bcf74c607c571625ee31' \ -d 'type=subscribe&fired_at=2026-08-30+11%3A21%3A57&data%5Bid%5D=8a25ff1d98&data%5Blist_id%5D=a6b5da1054&data%5Bemail%5D=ada%40example.com&data%5Bemail_type%5D=html&data%5Bip_opt%5D=203.0.113.7&data%5Bip_signup%5D=203.0.113.7&data%5Bmerges%5D%5BEMAIL%5D=ada%40example.com&data%5Bmerges%5D%5BFNAME%5D=Ada&data%5Bmerges%5D%5BLNAME%5D=Lovelace&data%5Bmerges%5D%5BINTERESTS%5D=Newsletter%2CProduct+updates'