Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Api Bundle Laravel Package

sendinblue/api-bundle

A PHP bundle that simplifies integrating Sendinblue’s API, giving applications a convenient way to configure credentials and access transactional email, SMS, and contact features with less boilerplate. Use it to centralize API setup and keep Sendinblue calls organized.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: sendinblue/api-bundle is a Symfony bundle, not a Laravel package. It ships a Bundle class, DependencyInjection/Configuration.php, and a Resources/config/services.xml — there is no ServiceProvider, no facade, no publishable config, and no config/app.php entry. Installing it in a Laravel app pulls symfony/framework-bundle and friends and gives you nothing you can boot. On Laravel 9+/10/11 (Symfony 6/7 components) it will typically fail dependency resolution outright.

So the day-to-day answer for a Laravel developer is: use the underlying SDK directly and treat the bundle as a reference implementation. The bundle is ~40 lines of XML service definitions; you can reproduce all of it in one service provider, and you keep control of lifetimes, config, and the Brevo rename.

Minimal setup:

composer require sendinblue/api-v3-sdk   # or getbrevo/brevo-php
// config/services.php
'sendinblue' => [
    'api_key' => env('SENDINBLUE_API_KEY'),
    'timeout' => (int) env('SENDINBLUE_TIMEOUT', 10),
],
// app/Providers/SendinblueServiceProvider.php
public function register(): void
{
    $this->app->singleton(Configuration::class, fn () => (new Configuration())
        ->setApiKey('api-key', config('services.sendinblue.api_key'))
        ->setApiKey('partner-key', config('services.sendinblue.api_key')));

    $this->app->singleton(TransactionalEmailsApi::class, fn ($app) =>
        new TransactionalEmailsApi(new Client(null, $app->make(Configuration::class)))
    );

    $this->app->bind(TransactionalMailer::class, SendinblueMailer::class);
}

Where to look first:

  1. vendor/sendinblue/api-v3-sdk/docs/ — every API class, model, and request shape lives here. The bundle never documented any of it, and neither will you get docs from it.
  2. vendor/sendinblue/api-bundle/Resources/config/services.xml — if you insist on mirroring the bundle's wiring, this is the only authoritative list of what it would have registered.
  3. Configuration.php in the SDK (setApiKey, setHost, setUserAgent) — the only config surface you actually care about.

First use case — transactional email: a SendinblueMailer service bound to a TransactionalMailer interface, building a SendSmtpEmail model, calling sendTransacEmail(), returning/logging the messageId. Everything else (contacts sync, campaigns, SMS) is the same shape with a different API class.

Implementation Patterns

Bind interfaces, not SDK classes. Declare App\Mail\Contracts\TransactionalMailer with domain methods (sendWelcome(User $u), sendPasswordReset(string $email, string $token)). Bind the SDK-backed implementation in the provider and typehint the interface everywhere. This is the pattern that survives the Sendinblue→Brevo rename, an SDK major bump, or a swap to Postmark — nothing above the binding changes.

Singleton the Configuration and Client, resolve APIs per use. Registering one Configuration with the API key set once means the key is read at container-build time from config/services.php, so php artisan config:cache works correctly. Never call env() from a service class — after config:cache it returns null.

Consider skipping the SDK entirely. For a Laravel app, the lowest-friction path is often the REST API via the Http facade:

Http::withHeaders(['api-key' => config('services.sendinblue.api_key')])
    ->retry(3, 200, throw: false)
    ->timeout(10)
    ->post('https://api.brevo.com/v3/smtp/email', $payload)
    ->throw();

You get Laravel's retry/backoff, timeout, Http::fake() in tests, and Http::pool() for batching — all the things the SDK and bundle lack. The tradeoff is losing the typed SendSmtpEmail model validation. For a handful of endpoints, this is usually the right call.

Queue everything that isn't user-blocking. A SendTransactionalEmail job with $tries = 5, $backoff = [10, 30, 60, 120], and retryUntil() gives you retries the SDK has none of. Combine with a Redis throttle for rate limits:

Redis::throttle('brevo')->allow(10)->every(1)->then(
    fn () => $this->send(),
    fn () => $this->release(10),
);

This is the single biggest operational win over the Symfony bundle's usage model — Laravel's queue is the retry and backpressure layer the package is missing.

Custom Notification channel. If you're already using Laravel notifications, a BrevoChannel (send($notifiable, Notification $notification)) wrapping your TransactionalMailer keeps email sends on the same rails as your other channels and makes them faked/swappable in tests via Notification::fake().

Artisan commands for ops. Contact list sync, campaign status, account credits — php artisan sendinblue:account injecting the relevant API class. Same idea as the bundle's console commands, native to Laravel, schedulable in routes/console.php / Kernel::schedule().

Map to models, don't pass arrays. Build SendSmtpEmail, CreateContact, CreateDoiContact from your Eloquent models in a small mapper class. The SDK models validate on construction and give your IDE something to work with; raw arrays silently produce 400s.

Fake it in tests. Bind a fake TransactionalMailer in TestCase::setUp(), or $this->mock(TransactionalEmailsApi::class). Never let a test hit the real API — there is no sandbox mode, so a leaked key in CI sends real mail to real contacts.

Gotchas and Tips

  • The 1.0.0 release has no changelog. The package was last published in 2017 (as 2017-09-12), so a jump to 1.0.0 with empty release notes is unverified change, not a stability signal. Pin an exact version in composer.json, diff vendor/sendinblue/api-bundle after update, and do not assume it's maintained going forward. For a Laravel app this matters less than it sounds, because you shouldn't be installing it anyway.
  • Don't try to install the bundle in Laravel. Even if Composer resolves, you get no service provider to register, so nothing is bound. Any "how do I use this in Laravel" answer is "you don't — you use the SDK."
  • config:cache will silently blank your key if you read env('SENDINBLUE_API_KEY') inside a service, provider closure that runs late, or job. Keep env() exclusively in config/*.php.
  • No retries, no rate-limit handling, no circuit breaker in the SDK. ApiException on 429/5xx. This is the top production failure mode. Centralize retry in the wrapper or in the queued job; never scatter it at call sites.
  • Exceptions are namespaced to the SDK. Catch Sendinblue\Client\ApiException, read getResponseBody() and getCode(), rethrow your own MailDeliveryException so callers aren't coupled to the package. Log the body — Brevo 400s are specific and useful.
  • Fail fast on a bad key. The bundle/SDK validates nothing; a wrong key surfaces as a runtime 401 deep in a request. Add php artisan sendinblue:health calling getAccount() and run it in your deploy/healthcheck.
  • Log the messageId on every accepted send, with your own correlation ID, via Log::withContext() or an explicit context array. Without it, "did this email go out?" is a manual search in the Brevo dashboard.
  • SDK version and the Brevo rename. sendinblue/api-v3-sdk was superseded by getbrevo/brevo-php, with namespace and some host/endpoint changes. Pick one, pin it, and don't have both in the lock file.
  • Long-running queue workers (Horizon) hold the singleton Configuration for the process lifetime. That's fine for a static API key, but if you ever rotate keys per tenant, resolve the API class per job instead of injecting the singleton into the job's constructor.
  • Extension point is your service provider, not the package. Anything you'd want to "configure" in the bundle (HTTP client, timeout, base host) is set on the SDK's Configuration object in your register(). There is no vendor override to maintain — a real advantage of not using the bundle.
  • Exit cost is one file. If you keep a TransactionalMailer interface and a single SDK-backed implementation, migrating to Http-based calls, Brevo's new SDK, or another provider is a change to one class plus one binding.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment