NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #4193 most downloaded on Packagist
API client library for the MailChimp
Last release 17 days ago
21 Sep 2026
Release timing varies
gaps range from 8 days to 11 months
Most releases are documented
notes for 48 of 54 stable releases
Nothing withdrawn
no release was ever pulled
10 years old
54 releases · first in 2016
Notify only a host that can listen ( #96 ) — the library called saveNotification() and getGmtDate() on the host's helper without checking that the hos
Fixed bugs
Notify only a host that can listen (#96) — the library called saveNotification() and getGmtDate() on the host's helper without checking that the host has them. On an extension older than 103.4.65 that is a fatal on every API call, and it has been since 3.0.42.
saveNotification() is the extension's, and it arrived there on 2024-12-03 in 103.4.65. The calls to it landed here the day after. Composer cannot hold that pairing together: an extension requires a minimum library version, a library cannot require a minimum extension version, and the extension's own constraint is a floor with no ceiling — so composer update hands an old extension the newest library published, and an app/code installation pairs the two under no constraint at all.
Both call sites are affected, which is to say both outcomes of an API call: the answer, in Mailchimp::call(), and the failure, in Mailchimp_Error::getFriendlyMessage(). The failure path is the worse of the two — it is reached precisely because Mailchimp answered 4xx, so an installation that could not listen lost the message explaining the failure and the request that was carrying it.
Guarded per method rather than per helper, which is the discipline the reporting path already followed in readContactAllowed() and readModuleVersion(). A host with no way to be notified is now left alone. getGmtDate() has been in the extension since 2018 and is guarded for the same reason rather than because it is likely to be absent: nothing here can assume the shape of the host it was handed.
A gate over src now walks every file and refuses a call on a helper method that file has never checked for, so a call site added later cannot reintroduce this quietly.
Note for extension authors
Nothing to do, and nothing to call. This release changes only what happens when the host is missing a method: a current extension gets the same payload, in the same call, in the same order, and cannot tell this release from the last by its behaviour.
Which is the shape of the fix generally — what it changes happens on installations too old to report that anything was ever wrong.
One column per quarter.
Let the host name the surface it is serving ( #93 ) — the beacon can now be told the application area and the action a process dispatched, through set
Implemented enhancements
Let the host name the surface it is serving (#93) — the beacon can now be told the application area and the action a process dispatched, through setSurface($area, $action).
Until this, the only thing separating a cron from a shopper's page render was PHP_SAPI, and that answers a different question. Magento's own cron entry point refuses to run under the CLI SAPI, so an installation running cron over HTTP is a web-SAPI process doing a full sync — indistinguishable, on the reporting side, from a page render.
Instance state, beside the user agent rather than on a bucket: a bucket exists only once an API key has been seen, and the dispatch happens whether or not one ever is. First non-empty wins, per token and independently, so a valid area survives an action that does not pass.
The action can be attacker-controlled, so the rule that bounds it is the substance of the change rather than a detail of it. A value is rejected whole, never repaired — stripping the bytes that failed yields something nobody sent, and possibly a route that exists. Length is measured on the raw bytes and before the charset test. The charset is ASCII-only, which also keeps one malformed byte from costing the entire report: json_encode() returns false on malformed UTF-8 and a body that is not a string is dropped. The pattern ends at \z rather than $, because PHP's $ also matches immediately before a final newline. And a token must carry at least one character that is not a separator, so an unrouted request — which composes the bare delimiters — names nothing and is refused.
Keep construction detail out of what the package ships (#94) — comment only, no behaviour. Docblocks that explained a rule by describing how the far end is built now explain it on this library's own terms, which is where every one of those rules actually stands.
Note for extension authors
setSurface() does nothing until a host calls it. An extension that never does produces the envelope it produced before, key for key. Guard the call with method_exists(): an app/code installation pairs whichever library is on disk with whichever extension is on disk, so a composer constraint decides nothing there.
Observe the audience collection, not just one audience ( #91 ) — observeList() kept only the first audience a process saw. An installation with severa
Implemented enhancements
Observe the audience collection, not just one audience (#91) — observeList() kept only the first audience a process saw. An installation with several store views described one of them, and nothing said how many existed. Coverage was a tautology — audiences we hold over audiences we hold — so the question that matters, whether an audience nobody looked at has changed, could not be asked.
The audience collection answers both halves in one response: it carries the account-wide count and each audience's own counts. Nothing new is requested — the collection is already fetched when the admin offers the audience dropdown.
The roster is keyed by audience id, so a later reading completes an earlier one rather than replacing it, and a repeated id is impossible by construction. Envelope keys: la (the roster, each entry carrying mem/uns/cln/tot) and lac (the account-wide count).
lac is never capped. A truncated roster beside an intact count still answers the coverage question; losing the count loses the only figure nothing else can supply. The roster itself is bounded twice — at 12 entries and at 4096 serialised bytes — and trimmed one entry at a time, so the bound costs the fewest audiences that satisfy it.
Observe the account's billing arrangement (#90) — pricing_plan_type sits on the account root response beside the four fields already read there, and separates an account that pays for a plan from one that pays nothing. Kept as the string the API returns rather than mapped to a fixed set, so a value not seen before arrives intact instead of collapsing into a default.
It is not the plan tier. Nothing on that response distinguishes the tiers, and the docblock says so, because that is the misreading the field invites.
Both are passive: no additional Mailchimp API call, no quota spent, and nothing added to any request.
Note for extension authors
lac arrives from the collection read only. An extension that never lists audiences reports the roster it built from single-audience reads and no account-wide count — which is the state this release exists to make visible, not a failure.
Take an audience's counts off a response the caller already asked for ( #88 ) — observeList() mirrors observeRoot() : passive, on the lists/{id} respo
Implemented enhancements
Take an audience's counts off a response the caller already asked for (#88) — observeList() mirrors observeRoot(): passive, on the lists/{id} response the extension's statistics job already reads every twelve hours. Measured on a running install: the same eight calls with the change as without, and nineteen more bytes downloaded.
getLists() gains include_total_contacts, appended last so every existing positional caller is unaffected. It is the billable figure, and the one the payload cannot yield by arithmetic — member_count + unsubscribe_count omits non-subscribed contacts, which on an ecommerce account is most of the audience. Absent is left null rather than derived.
total_contacts includes cleaned, measured on an audience with 631 of them. A consumer deriving the non-subscribed residual must subtract cleaned explicitly, and even then the residual is an upper bound rather than an exact figure: pending and archived land in it, and archived are never billed.
Only lists/{id} is observed, never a sub-resource — a member object carries its own stats, so a member fetch reached this and wrote nothing by luck rather than by design.
Notice a send that did not arrive, and say which windows were sampled (#87) — the reporting path discarded curl_exec()'s return and never read the status, so a refused connection, a timeout and a rejected envelope were all indistinguishable from delivery. A send that did not arrive now increments a counter carried on the next envelope as sfail.
It is a running total for the emitting process and nothing resets it: take the maximum per process, never a sum. A send cannot report its own failure, so a process whose every send fails still reports nothing; what becomes visible is the partial case.
The envelope also carries the sampler's two constants on the lane the sampler governs, as sr and sw, so a consumer predicting an installation's reporting cadence reads them instead of hardcoding them.
Note for extension authors
include_total_contacts does nothing until a caller asks for it. Until that lands, this reports the three counts that are not the billable figure and omits the one that is.
Fail the build when a deprecated call appears unguarded ( #83 ) — a deprecated call reached a release twice and a merchant found it both times. The ga…
Implemented enhancements
Share contact wherever nobody has declined, including hosts with no switch (#84) — contact sharing is on by default, and only an answer that says no is a refusal. The reporting path previously withheld the contact pair from a host it could not ask; every other kind of silence already read as permitted, and this makes the four consistent.
contact_unconfigured is now emitted alongside the pair rather than instead of it, and its meaning changes with it: from "no switch, so nothing was sent" to "no switch, sent anyway". Consumers reading it as "no contact for this installation" need to know.
Fail the build when a deprecated call appears unguarded (#83) — a deprecated call reached a release twice and a merchant found it both times. The gate tokenises rather than matching text, and has its own test pinning both directions.
Documentation
mc_store_id (#85) — a null means "no store was named in any request path this process made", not "this installation has no Mailchimp store". Nothing changes on the wire; the discriminator was already being reported.Tagged on develop, per this repository's convention.
Latest version (3.0.47) is not fully compatible with PHP 8.5 #79
Report how the Mailchimp API behaves for an installation #70
Implemented enhancements:
Undefined array key "instance" #68
Fixed bugs:
Implemented enhancements: Add member events
Implemented enhancements:
Merge pull request #67 from ebizmarts/Version-3.0.44
Merge pull request #67 from ebizmarts/Version-3.0.44
Error in composer.json for version 44
Implemented enhancements: Add timestamp and url #60
Implemented enhancements:
Add SaveNotification enhancement #58
Implemented enhancements:
Structure response of getFriendlyMessage #56
Implemented enhancements:
Show the complete url when an error happens #54
Add the instance value to the log #53
Implemented enhancements:
Add the possibility to add actions to ListMemberActivity #50
Implemented enhancements:
Implemented enhancements:
Add possibility to change the timeout #47
Implemented enhancements:
Full Changelog Fixed bugs: - Add php 8.2 compatibility \#44
Full Changelog Fixed bugs: - Add php 8.1 compatibility \#40
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Critical vendor/ebizmarts/mailchimp-lib/src/Mailchimp.php:222 \#20
In some circumstances $result is not an array \#16
Full Changelog
Export array 'errors' on call ended with errors \#12
Full Changelog
Php 7.2 icompatiblity with count \#9
Notice: Undefined index: detail in Mailchimp.php on line 222 \#8
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog Fixed bugs: - Error in API get Call \#1
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
Full Changelog
\* *This Change Log was automatically generated by github_changelog_generator*
* This Change Log was automatically generated by github_changelog_generator
Your coding agent can read these notes before it upgrades. Set up the MCP server →