How the webhook URL works. It is a public address that records whatever is sent to it and
answers 200. An inbox keeps the 50 most recent requests, accepts bodies up to 256 KB, and is
deleted 24 hours after it was created — or sooner if you press Delete URL or our server
restarts, because requests are held in memory and never written to disk.
Anyone who has the URL can read what it received. Use sandbox and test-mode accounts, do not route production traffic or customer data through it, and delete it when you are done. Privacy details.
What is a Mailchimp webhook?
A Mailchimp webhook is an HTTP POST sent to a URL you registered on an audience when a contact changes: someone subscribes, unsubscribes, updates their profile, changes their email address, or is cleaned after bouncing. A sixth event reports that a campaign finished sending. The body is application/x-www-form-urlencoded, not JSON, with the event name in type and the details in fields named like data[email].
Each webhook is also filtered by source: changes made by the subscriber, by an account admin, or through the API. A webhook that only listens for subscriber-initiated changes stays silent when your own code updates a contact through the API, which is the most common reason an expected event never shows up.
What a Mailchimp webhook request looks like
The request line and headers as they reach your server, followed by the body. Ids, tokens and the signature are placeholders.
POST /your/webhook/path HTTP/1.1
Host: your-server.example
Content-Type: application/x-www-form-urlencoded
User-Agent: MailChimp
type=subscribe&fired_at=2026-01-01+12%3A00%3A00&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.10&data%5Bip_signup%5D=203.0.113.10&data%5Bmerges%5D%5BEMAIL%5D=ada%40example.com&data%5Bmerges%5D%5BFNAME%5D=Ada&data%5Bmerges%5D%5BLNAME%5D=Lovelace
Decoded, that is type=subscribe, fired_at=2026-01-01 12:00:00, data[id] (the contact's id), data[list_id] (the audience), data[email], and the merge fields under data[merges][FNAME] and so on. PHP turns the brackets into nested arrays automatically; most other frameworks hand you flat keys that literally contain the brackets unless you enable an extended form parser. An upemail event carries data[old_email] and data[new_email] instead.
Headers Mailchimp sends
| Header | Example | What it is |
|---|---|---|
Content-Type | application/x-www-form-urlencoded | Form encoding. The body is not JSON. |
User-Agent | MailChimp | Identifies the sender. It is not proof of origin; nothing in the request is. |
Header names are case-insensitive. Your framework may show them lower-cased, and HTTP/2 always does.
Mailchimp does not sign webhooks
There is no signature header and no shared secret in the request. Anyone who learns the URL can post a convincing unsubscribe to it. The standard defence is to make the URL itself the secret: register an address that includes a long random value in the path or query string, serve it only over HTTPS, and reject any request that does not carry that value.
For anything that matters, treat the webhook as a hint rather than as the truth. Take the contact's email or id from the request and read the current state back from the Mailchimp API before acting, so a forged request can at worst cause a harmless lookup.
const crypto = require('crypto');
// Registered URL: https://your-server.example/hooks/mailchimp?key=<long random value>
// requestUrl: the path and query of the incoming request, e.g. req.originalUrl
function fromMailchimp(requestUrl, secret) {
const got = new URL(requestUrl, 'https://placeholder.invalid')
.searchParams.get('key') || '';
const a = Buffer.from(got);
const b = Buffer.from(secret);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
URL validation, retries and timeouts
When you save a webhook, Mailchimp first checks that the URL answers: it makes a request to the address and expects a 200. An endpoint that only accepts POST and answers anything else with 405 fails that check, so let other methods through with a plain success. The webhook URLs created on this site answer 200 to every method, so they pass.
Afterwards, a delivery succeeds only on a 200-range reply within roughly ten seconds. According to Mailchimp's documentation at the time of writing, a failed delivery is retried at increasing intervals over about an hour and a quarter, and a webhook that keeps failing can be switched off. Deliveries can repeat and can arrive out of order, so make the handler idempotent.
Catch a real Mailchimp webhook
- Press Create webhook URL at the top of this page and copy the address.
- In Mailchimp, open the audience you want to watch — a test audience is best — and find Webhooks in its settings.
- Create a webhook, paste the address as the callback URL, and tick the events and the sources (subscriber, admin, API) to send.
- Save. Mailchimp validates the URL; that request shows up in the inbox above as well.
- Add or unsubscribe a test contact and read the form-encoded event that arrives.
To run a caught request against your own code, select it and press Open in API Tester: the method, headers and body are loaded into the request console, where you enter your server's public URL and send it. A body you edit will no longer match its signature — that is the signature doing its job.
Things that catch people out
- API changes are silent by default. If the "via the API" source is not ticked, updates made by your own integration never fire the webhook.
fired_athas no timezone marker. It is a plainYYYY-MM-DD HH:MM:SSstring; do not let a date parser read it as local time.- Mailchimp Transactional is a separate system. Its webhooks (the former Mandrill) are JSON inside a form field and are signed, with an
X-Mandrill-Signatureheader. Code written for one does not fit the other.
api-tester.com is independent and is not affiliated with Mailchimp. Providers change header names, limits and retry schedules; treat the official Mailchimp documentation as the source of truth.
Related examples and tools
HubSpot Webhook Example
HubSpot delivers CRM events in batches: one request, a JSON array, each element a small notification that names a record without containing it.
Square Webhook Example
Square signs the notification URL together with the body, so a request that is perfectly genuine fails verification when your server disagrees about its own address.
Bitbucket Webhook Example
Bitbucket Cloud webhooks look like GitHub's at first glance and differ in the details that break copied code: the event name header, the signature header's name, and the shape of a push.
Jira Webhook Example
A Jira webhook delivers the whole issue plus a changelog of exactly what was edited.
Mailgun Webhook Example
Mailgun puts its signature inside the JSON body instead of in a header, and that signature covers a timestamp and a token rather than the event itself.
URL Encoder
Decode a form-encoded body to read the bracketed fields Mailchimp sent.
Curl POST Form
Send a form-encoded body like this one from a terminal.
Webhook Tester
The inbox on its own page, plus a guide to signatures, retries and idempotent handlers.
All Webhook Examples
30 providers side by side: content type, signature scheme and retry behaviour.
Other webhooks with no signature
References
- Official Mailchimp documentation — The provider's own guide to the requests described on this page.
- Mailchimp Developer: Transactional webhooks — The separate webhook system used by Mailchimp Transactional.
- Node.js: crypto.timingSafeEqual() — The constant-time comparison used in the code on this page.
These links leave api-tester.com, which is independent of the companies and projects they point to.
Mailchimp webhook FAQ
What does a Mailchimp webhook payload look like?
A form-encoded POST body with a type field such as subscribe or unsubscribe, a fired_at timestamp, and bracketed data fields such as data[email], data[list_id] and data[merges][FNAME].
Are Mailchimp webhooks signed?
No. Mailchimp Marketing audience webhooks have no signature header. Protect the endpoint by putting a long random secret in the URL, using HTTPS, and confirming changes through the API.
Which events can a Mailchimp webhook send?
subscribe, unsubscribe, profile (profile updates), upemail (email address changed), cleaned (address removed after bounces) and campaign (a campaign finished sending).
Why is my Mailchimp webhook not firing?
Most often because the source of the change is not enabled. Each webhook has separate switches for changes made by subscribers, by account admins and through the API.
Is a Mailchimp webhook JSON?
No. The body is application/x-www-form-urlencoded. Parse it as form data; the bracketed keys describe nested fields.