> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.brevo.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.brevo.com/_mcp/server.

# PHP SDK Changelog

## August 10, 2026 — v5.0.2

### Added

* **Email Campaigns — UTM parameter customization** — `EmailCampaigns::createEmailCampaign()` and `EmailCampaigns::updateEmailCampaign()` now accept optional `utmCampaign`, `utmContent`, and `utmTerm` fields in the request to customize UTM tracking parameters. Campaign retrieval methods return these values along with `utmId`, `utmMedium`, and `utmSource` when UTM tracking is enabled.
* **Transactional Email — per-contact pixel tracking consent** — `TransactionalEmails::sendTransacEmail()` now accepts optional `contactPixelTrackingConsent` boolean field on individual recipient objects (in `to`, `cc`, `bcc`, and `messageVersions` arrays) to control whether opens are tracked identifiably (`true`) or anonymized (`false`). When the per-contact pixel tracking consent feature is enabled for your account, this field determines tracking behavior per recipient.

Backward-compatible patch release. No breaking changes.

## July 3, 2026 — v5.0.1

### Added

* **Consent Groups Management** — new endpoints for managing contact consent groups:
  * `GET /contacts/consent-groups` — list all consent groups
  * `POST /contacts/consent-groups` — create a consent group
  * `GET /contacts/consent-groups/{id}` — retrieve consent group details
  * `PUT /contacts/consent-groups/{id}` — update consent group (name, description, signup mode)
  * `DELETE /contacts/consent-groups/{id}` — delete a consent group
* **Consent group support in contact operations** — `POST /contacts/import` now accepts optional `consentGroupIds` to add imported contacts to specified groups
* **Contact details enhancement** — `GET /contacts/{identifier}` now returns `consentGroups` array showing each group and subscription status (when feature enabled)
* **Wallet pass installation URLs** — `GET /wallet/passes/{passId}/installUrl/{contactId}` generates per-contact installation URLs for Apple Wallet and Google Wallet integration

### Fixed

* **Empty JSON object serialization** — `JsonSerializableType` now correctly serializes empty objects to `{}` instead of `[]`. Previously, objects with all optional properties unset would serialize as empty arrays, causing backend validation errors on strict type checkers. This affected both top-level objects and nested objects within parent types. The fix ensures proper JSON structure for all empty serializable objects.

Backward-compatible patch release. No breaking changes.

## June 15, 2026 — v4.0.16

### Fixed

* **`CreateBatchOrderResponse::batchId`** now deserializes correctly. The `createBatchOrder` endpoint returns the batch id as `batch_id` (snake\_case), but the SDK looked for `batchId`, leaving it unset and throwing `Cannot assign null to property ...::$batchId of type float` on every call. The wire mapping is corrected to `batch_id` and the property is now nullable (`?float`). The `$response->batchId` accessor is unchanged.

Backward-compatible patch release. No breaking changes.

## June 13, 2026 — v4.0.15

### Fixed

* **`GetCampaignStats`** count fields are now nullable (`?int`): `clickers`, `complaints`, `delivered`, `hardBounces`, `sent`, `softBounces`, `trackableViews`, `uniqueClicks`, `uniqueViews`, `unsubscriptions`, `viewed`. This model is reused for `statsByDomain` on `getEmailCampaign(s)`, where per-domain rows are sparse and the API returns `null` or omits some counts. The previous non-nullable typing caused a `TypeError` while deserializing `statsByDomain` (`globalStats` was unaffected). ([#138](https://github.com/getbrevo/brevo-php/issues/138))

Backward-compatible patch release. No breaking changes.

## June 12, 2026 — v4.0.14

### Fixed

* **`GetTransacBlockedContactsResponseContactsItem::senderEmail`** is now nullable (`?string`). The API returns `null` for some blocked or unsubscribed contacts, which previously caused a `TypeError`. ([#137](https://github.com/getbrevo/brevo-php/issues/137))
* **`campaignId`** is now nullable (`?int`) on `GetContactInfoResponseStatisticsUnsubscriptionsUserUnsubscriptionItem` and `GetContactStatsResponseUnsubscriptionsUserUnsubscriptionItem`. Form-based (non-campaign) unsubscriptions return `null`, which previously caused a `TypeError`. ([#136](https://github.com/getbrevo/brevo-php/issues/136))
* **Guzzle timeout support** — the PSR-18 client detection used `class_exists` on an interface and never matched; corrected to `interface_exists`, so the `timeout` option is now honored with a Guzzle client instead of emitting a warning. ([#133](https://github.com/getbrevo/brevo-php/issues/133))

Backward-compatible patch release. No breaking changes.

## May 14, 2026 — v5.0.0

### Breaking changes

* ⚠️ **`GetCompaniesRequest::filters`** — renamed to `filtersAttributesName`. Existing code passing `'filters' => ...` does not throw, but the filter is silently ignored server-side and the response is unfiltered.
* **`Event::createBatchEvents`** — first argument changed from `array` to a `CreateBatchEventsRequest` wrapper.
* **`Balance::getActiveBalancesApi`** — return type changed from `?BalanceLimit` to `?GetLoyaltyBalanceProgramsPidActiveBalanceResponse` (different shape).
* **`Balance::getContactBalances`** — `$request` no longer defaults to empty; `balanceDefinitionId` is required.
* **`Balance\BeginTransactionRequest::eventTime`** — type changed from `?string` to `?DateTime`. ISO strings now cause `TypeError`.
* **`Tasks::getAllTaskTypes`** — return type changed from `?GetCrmTasktypesResponse` (wrapper) to `?array<GetCrmTasktypesResponseItem>`.
* **`EmailCampaigns\GetEmailCampaignResponse::utmIdActive`** renamed to `utmId` (wire key `utmIDActive` → `utmID`, type `?bool` → `?int`). Same change on `GetEmailCampaignsResponseCampaignsItem`.
* Model fields removed: `GetAccountResponse::dateTimePreferences`, `Webhook::channel`, `GetProcessResponseInfo::export`, `GetProcessResponse::error/createdAt/completedAt`, and several `ExternalFeeds` response fields.
* `Process\GetProcessResponseInfoImport`: count fields changed from `?int` to `?string` (now URLs to CSV reports) — affects `invalidEmails`, `duplicateContactId`, `duplicateExtId`, etc.
* `Ecommerce\CreateUpdateProductResponse::id` and `CreateUpdateCategoryResponse::id`: `?int` → `?string`.
* Several `string` date fields tightened to `?DateTime` (`Program::*`, `Reward::*`, `BalanceLimit::createdAt`/`updatedAt`).
* `ConversationsMessageAttachmentsItem`: `fileName` → `name`, `inlineId` → `link`.
* `CustomObjects` associations union flattened: `UpsertrecordsRequestRecordsItemAssociationsItem` is now a single class with `action: 'link' | 'unlink'` discriminator; 7 old variant classes deleted.

### Added

* New optional fields and filters across `contacts->createContact`, `contacts->updateContact`, `emailCampaigns->getEmailCampaigns`, `ecommerce->getProducts`, and several other endpoints.
* Tier groups support upgrade and downgrade schedule fields.

See the [Upgrading from v4.x guide](https://github.com/getbrevo/brevo-php#upgrading-from-v4x) for migration details and a one-line rollback pin.

## April 10, 2026 — v4.0.13

### Fixed

* **Process endpoints** — `duplicate_email_id` is now typed as a string (URL to a CSV file) instead of an integer. The legacy `in_process` status value is now handled correctly.
* **Event endpoints** — `contact_properties` and `event_properties` now accept boolean values in `createEvent` and `createBatchEvents`.
* **Contact endpoints** — `attributes` now accepts plain integers in `createContact` and `updateContact`. PHP users no longer need to cast integers to `float`.

## March 25, 2026 — v4.0.11

### Added

* `$client->event->getEvents()` — retrieve a paginated list of custom events with filters for contact, event name, object type, and date range.
* `$client->event->createBatchEvents()` — track multiple contact interactions in a single request.
* `PatchCrmAttributesIdRequest` — update a CRM attribute's display label and option labels.
* `CreateUpdateProductRequest` and batch product upsert now accept optional `brand` and `description` fields.
* Balance: `getActiveBalancesApi()`, `getContactBalances()`, and `getSubscriptionBalances()` now accept `includeInternal`.
* Balance: `getTransactionHistory()` now supports filtering by `status` and `transactionType`.

### Improved

* `meta` field on balance definition requests is now a typed class instead of a generic array.
* `metaInfo` size limit for products clarified: maximum **20,000 characters** total.
* `users` field on `GetAccountResponsePlanVerticalsItem` is now nullable.

### Fixed

* `createContact()` no longer throws `BrevoException: Failed to deserialize response: Syntax error` on empty success responses.

## March 12, 2026 — v4.0.10

### Added

* Batch events endpoint: `$client->event->createBatchEvents()` to track multiple contact interactions in a single request.
* `getEmailCampaign()` accepts a new optional `excludeHtmlContent` flag to omit the HTML body from the response.

### Fixed

* `UpdateContactRequest` and `CreateContactRequest`: boolean values in union-typed attribute maps now serialize correctly. Previously threw `JsonException: Cannot serialize value of type boolean`.
* `Order::products`: `OrderProductsItem` now exposes all product fields — `price`, `productId`, `variantId`, `quantity`, `quantityFloat`.
* `GetCampaignStats`: `appleMppOpens` and `opensRate` are now correctly typed as nullable (`?int`, `?float`).

### Deprecated

* `sendTransacSms()` — use `sendAsyncTransactionalSms()` instead.

## February 27, 2026 — v4.0 release

Released the PHP SDK v4 (`getbrevo/brevo-php`) with a type-safe client for the Brevo API.

```bash
composer require getbrevo/brevo-php guzzlehttp/guzzle
```

### Key highlights

* Unified client via `new Brevo(apiKey: "...")` — single entry point with namespaced service clients
* Strongly typed request and response objects with full PHPDoc annotations
* PSR-18 HTTP client support — use Guzzle, Symfony HttpClient, or any compatible client
* Automatic retries with exponential backoff (configurable per client or per request)
* Structured errors via `BrevoApiException` with `getCode()`, `getBody()`, and `getMessage()`
* 33 service namespaces covering the entire Brevo API