> 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

## Overview

The Brevo PHP SDK (`getbrevo/brevo-php`) is a type-safe client library for the Brevo API. It provides:

* A unified `Brevo` client with namespaced service clients
* Strongly typed request and response objects with full PHPDoc annotations
* PSR-18 HTTP client compatibility (Guzzle, Symfony HttpClient, etc.)
* Automatic retries with exponential backoff
* Structured error handling via `BrevoApiException`

[![Packagist](https://img.shields.io/packagist/dm/getbrevo/brevo-php)](https://packagist.org/packages/getbrevo/brevo-php)

Version 5.0 introduces breaking changes versus v4.x. v4.x remains supported and continues to receive wire-compatibility fixes — see the [changelog](/guides/php/changelog) for the list of changes and the README for migration steps.

## Requirements

* PHP 8.1+
* `ext-json`
* A PSR-18 HTTP client (e.g., Guzzle, Symfony HttpClient)

## Installation

Install the SDK using Composer:

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

Guzzle is recommended as the HTTP client:

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

If no HTTP client is provided, the SDK uses `php-http/discovery` to auto-detect an installed PSR-18 client.

## Quick start

Initialize the client and send your first email:

```php title="quick_start.php"
<?php

require_once __DIR__ . '/vendor/autoload.php';

use Brevo\Brevo;
use Brevo\TransactionalEmails\Requests\SendTransacEmailRequest;
use Brevo\TransactionalEmails\Types\SendTransacEmailRequestSender;
use Brevo\TransactionalEmails\Types\SendTransacEmailRequestToItem;

$client = new Brevo(apiKey: 'your-api-key');

$result = $client->transactionalEmails->sendTransacEmail(
    new SendTransacEmailRequest([
        'subject' => 'Hello from Brevo!',
        'htmlContent' => '<html><body><p>Hello,</p><p>This is my first transactional email.</p></body></html>',
        'sender' => new SendTransacEmailRequestSender([
            'name' => 'Alex from Brevo',
            'email' => 'hello@brevo.com',
        ]),
        'to' => [
            new SendTransacEmailRequestToItem([
                'email' => 'johndoe@example.com',
                'name' => 'John Doe',
            ]),
        ],
    ])
);

echo 'Email sent. Message ID: ' . $result->messageId . PHP_EOL;
```

## Configuration

Pass an options array as the second argument to configure the client:

```php title="configuration.php"
$client = new Brevo('your-api-key', [
    'timeout'    => 30,  // seconds
    'maxRetries' => 3,
]);
```

### Constructor parameters

| Parameter    | Type                    | Default  | Description                                                                   |
| ------------ | ----------------------- | -------- | ----------------------------------------------------------------------------- |
| `apiKey`     | `string`                | Required | Your Brevo API key                                                            |
| `timeout`    | `float`                 | `null`   | Request timeout in seconds. Defaults to the underlying HTTP client's default. |
| `baseUrl`    | `string`                | `null`   | Override the default API base URL                                             |
| `maxRetries` | `int`                   | `2`      | Maximum retry attempts on retryable errors                                    |
| `client`     | `ClientInterface`       | `null`   | Custom PSR-18 HTTP client                                                     |
| `headers`    | `array<string, string>` | `null`   | Additional default headers sent with every request                            |

## Error handling

`BrevoApiException` is thrown for any non-2xx HTTP response. Catch it to inspect the status code and response body:

```php title="error_handling.php"
use Brevo\Exceptions\BrevoApiException;
use Brevo\Exceptions\BrevoException;

try {
    $client->transactionalEmails->sendTransacEmail($request);
} catch (BrevoApiException $e) {
    $statusCode = $e->getCode();
    $body       = $e->getBody();

    if ($statusCode === 401) {
        echo 'Invalid API key';
    } elseif ($statusCode === 429) {
        echo 'Rate limited. Check Retry-After header.';
    } else {
        echo "API error {$statusCode}: " . $e->getMessage();
    }
} catch (BrevoException $e) {
    echo 'SDK error: ' . $e->getMessage();
}
```

### Exception classes

| Class               | Description                         |
| ------------------- | ----------------------------------- |
| `BrevoApiException` | Thrown for non-2xx HTTP responses   |
| `BrevoException`    | Base class for all SDK-level errors |

`BrevoApiException` exposes:

* `getCode()` — HTTP status code
* `getMessage()` — Error message
* `getBody()` — Parsed response body

### Status codes

| Code  | Meaning              |
| ----- | -------------------- |
| `400` | Bad Request          |
| `401` | Unauthorized         |
| `403` | Forbidden            |
| `404` | Not Found            |
| `422` | Unprocessable Entity |
| `429` | Too Many Requests    |
| `5xx` | Server Error         |

## Retries

Automatic retries with exponential backoff are enabled by default (2 retries). Configure at the client or request level:

```php title="retries.php"
// Client-level
$client = new Brevo('your-api-key', [
    'maxRetries' => 3,
]);

// Request-level (overrides client setting)
$client->transactionalEmails->sendTransacEmail($request, [
    'maxRetries' => 0, // Disable retries for this request
]);
```

### Retry behavior

* **Retryable status codes**: `408`, `429`, `5xx`
* **Backoff schedule**: \~1s, \~2s, \~4s (exponential, base 1000ms) with ±10% symmetric jitter
* **Maximum delay**: 60s per retry interval
* **Rate limit headers**: Respects `Retry-After` and `X-RateLimit-Reset` response headers

## Timeouts

No default timeout is configured. Unless you set one, the underlying HTTP client's default applies (Guzzle defaults to no timeout). Set an explicit timeout at the client or request level:

```php title="timeouts.php"
// Client-level
$client = new Brevo('your-api-key', [
    'timeout' => 30,
]);

// Request-level (overrides client setting)
$client->transactionalEmails->sendTransacEmail($request, [
    'timeout' => 10,
]);
```

Timeout forwarding is supported for Guzzle and Symfony HttpClient. For other PSR-18 clients, the timeout value is ignored and a PHP warning is triggered.

### Recommended timeout values

| Use case                | Timeout    |
| ----------------------- | ---------- |
| Standard API calls      | `30–60s`   |
| Quick operations        | `10–15s`   |
| Bulk operations         | `120–300s` |
| Real-time / low-latency | `5–10s`    |

## Request options

The `timeout`, `maxRetries`, and `headers` options can be overridden per request by passing an options array as the last argument:

```php title="request_options.php"
$client->transactionalEmails->sendTransacEmail($request, [
    'timeout'    => 10,
    'maxRetries' => 1,
    'headers'    => [
        'X-Custom-Header' => 'custom-value',
    ],
]);
```

### Query parameters

Pass query parameters via the typed request object:

```php title="query_params.php"
use Brevo\Contacts\Requests\GetContactsRequest;

$client->contacts->getContacts(new GetContactsRequest([
    'limit'  => 50,
    'offset' => 0,
]));
```

## Binary responses

Some endpoints (e.g., attachment downloads) return binary content directly:

```php title="binary_response.php"
$content = $client->inboundParsing->getInboundEmailAttachment($downloadToken);

file_put_contents('path/to/file', $content);
```

## Type safety

All request and response objects are strongly typed. Use the generated request classes for IDE autocomplete and static analysis:

```php title="type_safety.php"
use Brevo\TransactionalEmails\Requests\SendTransacEmailRequest;
use Brevo\TransactionalEmails\Types\SendTransacEmailRequestSender;
use Brevo\TransactionalEmails\Types\SendTransacEmailRequestToItem;

$request = new SendTransacEmailRequest([
    'subject'     => 'First email',
    'textContent' => 'Hello world!',
    'sender'      => new SendTransacEmailRequestSender([
        'name'  => 'Bob Wilson',
        'email' => 'bob.wilson@brevo.com',
    ]),
    'to' => [
        new SendTransacEmailRequestToItem([
            'email' => 'sarah.davis@example.com',
            'name'  => 'Sarah Davis',
        ]),
    ],
]);
```

## Custom HTTP client

Pass any PSR-18-compatible client via the `client` option:

```php title="custom_http_client.php"
use Brevo\Brevo;
use GuzzleHttp\Client;

$client = new Brevo('your-api-key', [
    'client' => new Client(['timeout' => 5.0]),
]);
```

### Common integrations

#### Guzzle

```php
use GuzzleHttp\Client;

$client = new Brevo('your-api-key', [
    'client' => new Client(['timeout' => 5.0]),
]);
```

#### Symfony HttpClient

```php
use Symfony\Component\HttpClient\Psr18Client;
use Symfony\Component\HttpClient\HttpClient;

$client = new Brevo('your-api-key', [
    'client' => new Psr18Client(
        HttpClient::create(['timeout' => 5.0])
    ),
]);
```

#### Laravel

Register the client as a singleton in a service provider:

```php
// AppServiceProvider.php
use Brevo\Brevo;

public function register(): void
{
    $this->app->singleton(Brevo::class, function () {
        return new Brevo(apiKey: config('services.brevo.api_key'));
    });
}
```

## Logging

The SDK doesn't ship a built-in logger. Instead, it routes every request through the PSR-18 HTTP client you pass via the `client` option, which means you can plug in **any PSR-3 logger** (Monolog, Symfony Logger, Laravel's `Log` facade, etc.) by wrapping that client with logging middleware.

This keeps the SDK lean and lets you reuse the logger your application already configures.

```php title="logging_guzzle.php"
use Brevo\Brevo;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\MessageFormatter;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$logger = new Logger('brevo');
$logger->pushHandler(new StreamHandler('php://stderr', Logger::DEBUG));

$stack = HandlerStack::create();
$stack->push(Middleware::log(
    $logger,
    new MessageFormatter('{method} {uri} → {code} ({res_header_Content-Length} bytes)')
));

$client = new Brevo('your-api-key', [
    'client' => new Client(['handler' => $stack, 'timeout' => 5.0]),
]);
```

### What gets logged

Logging happens at the HTTP layer, so each Brevo SDK call produces a log line per HTTP request — including **retries**, which the SDK performs internally. Use the message formatter to control verbosity (method/URI only, full headers, request and response bodies, timing, etc.).

`Middleware::log` placeholders worth knowing:

| Placeholder                       | Description                                    |
| --------------------------------- | ---------------------------------------------- |
| `{method}`                        | HTTP method (`GET`, `POST`, …)                 |
| `{uri}`                           | Full request URI                               |
| `{code}`                          | Response status code                           |
| `{req_headers}` / `{res_headers}` | Full headers (be careful — includes `api-key`) |
| `{req_body}` / `{res_body}`       | Request and response bodies                    |
| `{error}`                         | Error message when a network error occurs      |

The `api-key` header is sent on every request. If you log request headers, redact it before writing to disk or shipping to an external sink. The simplest approach is a custom middleware that strips the header before the log middleware runs.

### Common integrations

#### Symfony HttpClient + Symfony Logger

Symfony's `Psr18Client` accepts a PSR-3 logger via the underlying `HttpClient`. It logs requests, responses, and retries automatically — no middleware wiring required.

```php
use Brevo\Brevo;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\Psr18Client;
use Symfony\Component\HttpClient\LoggerAwareInterface;

$http = HttpClient::create(['timeout' => 5.0]);
$http->setLogger($yourPsr3Logger); // Monolog, Symfony Logger, anything PSR-3

$client = new Brevo('your-api-key', [
    'client' => new Psr18Client($http),
]);
```

#### Laravel (resolve the logger from the container)

Reuse Laravel's configured logger by resolving it from the service container when you build the Brevo client:

```php
// AppServiceProvider.php
use Brevo\Brevo;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\MessageFormatter;
use Psr\Log\LoggerInterface;

public function register(): void
{
    $this->app->singleton(Brevo::class, function ($app) {
        $stack = HandlerStack::create();
        $stack->push(Middleware::log(
            $app->make(LoggerInterface::class),
            new MessageFormatter('{method} {uri} → {code}')
        ));

        return new Brevo(
            apiKey: config('services.brevo.api_key'),
            options: ['client' => new Client(['handler' => $stack])],
        );
    });
}
```

#### Custom PSR-3 logger (any framework)

Any object implementing `Psr\Log\LoggerInterface` works with Guzzle's `Middleware::log`. This lets you drop the SDK into projects that use Monolog, the Symfony Logger, Laminas Log, or a hand-rolled PSR-3 implementation without changing the SDK call sites.

## PHP-specific considerations

### API key from environment

Avoid hardcoding API keys. Use environment variables:

```php title="env_api_key.php"
$client = new Brevo(apiKey: $_ENV['BREVO_API_KEY'] ?? getenv('BREVO_API_KEY'));
```

In Laravel, use `config('services.brevo.api_key')`. In Symfony, use `$_ENV['BREVO_API_KEY']` or service parameters.

### Memory limits

For large operations (bulk exports, large attachments), adjust PHP's `memory_limit`:

```php
ini_set('memory_limit', '256M');
```

## Available services

The unified `Brevo` client exposes the following service namespaces:

| Property                | Description                                                 |
| ----------------------- | ----------------------------------------------------------- |
| `transactionalEmails`   | Send emails, manage templates, blocked contacts and domains |
| `transactionalSms`      | Send SMS messages and view delivery statistics              |
| `transactionalWhatsApp` | Send WhatsApp messages and view event reports               |
| `smsTemplates`          | Manage SMS templates                                        |
| `contacts`              | Manage contacts, lists, folders, attributes and segments    |
| `emailCampaigns`        | Create and manage email marketing campaigns                 |
| `smsCampaigns`          | Create and manage SMS marketing campaigns                   |
| `whatsAppCampaigns`     | Create and manage WhatsApp campaigns and templates          |
| `companies`             | Manage CRM companies                                        |
| `deals`                 | Manage CRM deals and pipelines                              |
| `tasks`                 | Manage CRM tasks                                            |
| `notes`                 | Manage CRM notes                                            |
| `files`                 | Upload and manage CRM files                                 |
| `conversations`         | Manage conversation messages and automated messages         |
| `ecommerce`             | Manage products, categories, orders and attribution         |
| `coupons`               | Manage coupon collections and coupons                       |
| `payments`              | Create and manage payment requests                          |
| `event`                 | Track custom events                                         |
| `webhooks`              | Manage webhooks                                             |
| `senders`               | Manage senders and IPs                                      |
| `domains`               | Manage and authenticate domains                             |
| `account`               | Retrieve account information and activity logs              |
| `inboundParsing`        | Retrieve inbound email events and attachments               |
| `customObjects`         | Manage custom object records                                |
| `externalFeeds`         | Manage external RSS feeds                                   |
| `masterAccount`         | Manage sub-accounts and groups (enterprise)                 |
| `user`                  | Manage users and permissions                                |
| `process`               | Retrieve background process status                          |
| `program`               | Manage loyalty programs                                     |
| `balance`               | Manage loyalty balances and transactions                    |
| `reward`                | Manage loyalty rewards and vouchers                         |
| `tier`                  | Manage loyalty tiers and tier groups                        |

## Migration from the legacy SDK

### Key changes

| Area        | Legacy SDK                                         | v4.x                                              |
| ----------- | -------------------------------------------------- | ------------------------------------------------- |
| Import      | `use Brevo\Client\Api\*`                           | `use Brevo\Brevo`                                 |
| Client init | `new TransactionalEmailsApi($httpClient, $config)` | `new Brevo(apiKey: 'api-key')`                    |
| Config      | `Configuration::getDefaultConfiguration()`         | Constructor options array                         |
| Requests    | Setter methods (`->setSubject(...)`)               | Typed request objects                             |
| Errors      | Guzzle exceptions                                  | `BrevoApiException` with `getCode()`, `getBody()` |
| HTTP client | Hard Guzzle dependency                             | PSR-18 (any compatible client)                    |
| Retries     | Not built-in                                       | Automatic with exponential backoff                |
| PHP         | 7.x+                                               | 8.1+                                              |

### Migration example

```php title="legacy_example.php"
// Legacy SDK
use Brevo\Client\Configuration;
use Brevo\Client\Api\TransactionalEmailsApi;
use Brevo\Client\Model\SendSmtpEmail;

$config = Configuration::getDefaultConfiguration()
    ->setApiKey('api-key', 'xkeysib-xxx');

$api = new TransactionalEmailsApi(new \GuzzleHttp\Client(), $config);

$message = new SendSmtpEmail();
$message->setSubject('First email');
$message->setTextContent('Hello world!');
$message->setSender(['name' => 'Bob Wilson', 'email' => 'bob.wilson@brevo.com']);
$message->setTo([['email' => 'sarah.davis@example.com', 'name' => 'Sarah Davis']]);

$api->sendTransacEmail($message);
```

```php title="v4_example.php"
// v4.x
use Brevo\Brevo;
use Brevo\TransactionalEmails\Requests\SendTransacEmailRequest;
use Brevo\TransactionalEmails\Types\SendTransacEmailRequestSender;
use Brevo\TransactionalEmails\Types\SendTransacEmailRequestToItem;

$client = new Brevo(apiKey: 'xkeysib-xxx');

$client->transactionalEmails->sendTransacEmail(
    new SendTransacEmailRequest([
        'subject'     => 'First email',
        'textContent' => 'Hello world!',
        'sender'      => new SendTransacEmailRequestSender([
            'name'  => 'Bob Wilson',
            'email' => 'bob.wilson@brevo.com',
        ]),
        'to' => [
            new SendTransacEmailRequestToItem([
                'email' => 'sarah.davis@example.com',
                'name'  => 'Sarah Davis',
            ]),
        ],
    ])
);
```

## Resources

* [GitHub Repository](https://github.com/getbrevo/brevo-php/tree/main)
* [Packagist Package](https://packagist.org/packages/getbrevo/brevo-php)
* [API Reference](https://developers.brevo.com/reference)
* [Support](mailto:support@brevo.com)