Parse and handle Mailchimp webhook payloads. Typed events, secret verification, event listeners, zero dependencies, PHP 8.5+.
Author: Chimpmatic
Mailchimp sends webhook notifications when subscribers join, leave, update their profile, change their email, or get cleaned from your audience. Parsing these payloads means dealing with nested form data, extracting merge fields, and routing events to the right handler.
This library turns raw $_POST data into typed, readonly objects with a clean event listener API.
Built for Contact Form 7 to Mailchimp integrations, but works with any PHP application.
- PHP 8.5 or higher (uses
readonlyclasses and enums) - No extensions required (zero dependencies)
A fatal error (E_USER_ERROR) is triggered if loaded on PHP < 8.5. The library will not run on older versions.
composer require chimpmatic/mailchimp-webhook-handleruse Chimpmatic\WebhookHandler\WebhookHandler;
use Chimpmatic\WebhookHandler\WebhookEvent;
use Chimpmatic\WebhookHandler\WebhookPayload;
$handler = new WebhookHandler();
// Register listeners
$handler->on(WebhookEvent::Subscribe, function (WebhookPayload $payload): void {
echo "New subscriber: {$payload->email}";
echo "Name: {$payload->getMergeField('FNAME')} {$payload->getMergeField('LNAME')}";
});
$handler->on(WebhookEvent::Unsubscribe, function (WebhookPayload $payload): void {
echo "Unsubscribed: {$payload->email} — reason: {$payload->getReason()}";
});
// Handle the incoming webhook
$payload = $handler->handle($_POST);
echo $payload->getSummary();| Event | Mailchimp Type | Description |
|---|---|---|
Subscribe |
subscribe |
New subscriber added |
Unsubscribe |
unsubscribe |
Subscriber removed |
ProfileUpdate |
profile |
Subscriber updated their profile |
EmailChanged |
upemail |
Subscriber changed their email |
Cleaned |
cleaned |
Email bounced (hard/soft) |
Campaign |
campaign |
Campaign sent to list |
use Chimpmatic\WebhookHandler\WebhookHandler;
use Chimpmatic\WebhookHandler\WebhookException;
$handler = new WebhookHandler('your-webhook-secret');
// Mailchimp sends a GET request to validate the URL
if ($handler->isValidationRequest($_SERVER['REQUEST_METHOD'])) {
http_response_code(200);
exit;
}
// Verify the secret (Mailchimp appends ?secret=... to the URL)
if (!$handler->verifySecret($_GET['secret'] ?? '')) {
http_response_code(403);
exit;
}
try {
$payload = $handler->handle($_POST);
http_response_code(200);
} catch (WebhookException $e) {
http_response_code(400);
echo $e->getMessage();
}$handler->on(WebhookEvent::Subscribe, function (WebhookPayload $payload): void {
// Basic info
echo $payload->email; // "john@example.com"
echo $payload->audienceId; // "abc123def4"
echo $payload->firedAt; // "2026-03-16 21:00:00"
echo $payload->event->value; // "subscribe"
// Merge fields
echo $payload->getMergeField('FNAME'); // "John"
echo $payload->getMergeField('LNAME'); // "Doe"
// Full summary
echo $payload->getSummary();
// "[2026-03-16 21:00:00] subscribe — john@example.com (audience: abc123def4)"
});$handler->on(WebhookEvent::EmailChanged, function (WebhookPayload $payload): void {
echo "Old: {$payload->getOldEmail()}"; // "old@example.com"
echo "New: {$payload->getNewEmail()}"; // "new@example.com"
});$handler->onAny(function (WebhookPayload $payload): void {
// Log every webhook
error_log($payload->getSummary());
});// Just parse, don't trigger listeners
$payload = $handler->parse($_POST);
if ($payload->isSubscribe()) {
// ...
}use Chimpmatic\WebhookHandler\WebhookException;
try {
$handler->parse($_POST);
} catch (WebhookException $e) {
// Empty payload, missing type, unknown event type, invalid data
}- mailchimp-api-key-validator — Validate API key format and connectivity
- mailchimp-audience-finder — Find Audience IDs and merge fields
- mailchimp-subscriber — Subscribe contacts with merge fields
- Packagist
- GitHub
- Issues
- Connect Contact Form 7 with Mailchimp
- How to Get Your Mailchimp API Key
- Mailchimp Audience Fields and Merge Tags
- Chimpmatic — Contact Form 7 to Mailchimp
MIT License. Copyright (c) 2026 Chimpmatic.