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

Twilio Bundle Laravel Package

vresh/twilio-bundle

A Symfony bundle that integrates the Twilio SDK, providing configurable services for sending SMS, making voice calls, and accessing Twilio APIs directly from your Laravel or Symfony application.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it is not a Laravel package. vresh/twilio-bundle is a Symfony 2/3 bundle — it ships a Bundle class, DependencyInjection/Extension, and a YAML config tree. There is no ServiceProvider, no extra.laravel block, and no auto-discovery. Release 1.0.2 pins twilio/sdk to 4.* explicitly, which turns the bundle's "ancient SDK" problem from a side effect into a hard constraint: it now deliberately refuses SDK 5/6/7. In a Laravel app that pin will fail composer require against anything modern (SDK 4.x pulls PHP 5.5-era constraints and old Guzzle, conflicting with Laravel's Guzzle 6/7 and the HTTP client). Don't install it.

What you should actually do day-to-day:

  1. composer require twilio/sdk (latest 7.x) in the Laravel app.
  2. Read this bundle as a reference for wiring, not as a dependency. Its entire value proposition is "read two config keys, build a Twilio\Rest\Client, register it in the container." That is ~40 lines you will reimplement in a service provider.

If you are, unusually, working inside a Symfony 2.8/3.x app that already uses it:

composer require vresh/twilio-bundle  # resolves twilio/sdk 4.* only
// app/AppKernel.php
$bundles[] = new Vresh\TwilioBundle\VreshTwilioBundle();
# app/config/config.yml — verify key names in DependencyInjection/Configuration.php,
# they have varied between releases (sid/token vs account_sid/auth_token)
vresh_twilio:
    sid:   "%twilio_sid%"
    token: "%twilio_token%"

Then confirm the service id rather than guessing — it is typically vresh_twilio or vresh_twilio.wrapper:

app/console debug:container | grep -i twilio

First real use case (Symfony side): inject the wrapper and send one SMS; from there it is just the (4.x) Twilio SDK underneath — note that SDK 4.x resource accessors differ from the docs on twilio.com today, which assume 5.x+.

Laravel equivalent — your starting point:

// config/services.php
'twilio' => [
    'sid'   => env('TWILIO_SID'),
    'token' => env('TWILIO_TOKEN'),
    'from'  => env('TWILIO_FROM'),
],
// app/Providers/TwilioServiceProvider.php
public function register(): void
{
    $this->app->singleton(TwilioClient::class, fn () => new TwilioClient(
        config('services.twilio.sid'),
        config('services.twilio.token'),
    ));
}

Implementation Patterns

Singleton client, injected. The bundle's only structural idea is "one client per container, credentials from config." Mirror it with $this->app->singleton(...) and constructor-inject TwilioClient into controllers/jobs. Never new TwilioClient(...) inline — you lose config overrides and testability.

Facade only if you must. This bundle-style usage ($this->get('vresh_twilio')->account->messages->create(...)) works, but in Laravel prefer a thin domain service over the SDK so call sites don't depend on Twilio's fluent API — and so the 4.x-vs-7.x resource-accessor divergence never leaks into your app:

class SmsSender
{
    public function __construct(private TwilioClient $client) {}

    public function send(string $to, string $body): string
    {
        $msg = $this->client->messages->create($to, [
            'from' => config('services.twilio.from'),
            'body' => $body,
            'statusCallback' => route('twilio.status'),
        ]);
        return $msg->sid;
    }
}

Queue the send. SMS is an outbound HTTP call to a third party; it belongs in a job, not a request cycle. The bundle gives you nothing here (no events, no async) — in Laravel use ShouldQueue, $tries = 3, backoff(), and treat Twilio\Exceptions\RestException codes 20429 (rate limit) and 21610 (recipient opted out) as non-retryable.

Notifications channel. If you're on Laravel Notifications, write a custom channel wrapping SmsSender so via() returns ['sms'] alongside mail/database. This is the pattern you'd use 90% of the time; the bundle has no analogue.

Webhooks are your job. Inbound SMS and delivery-status callbacks land on a route the bundle does not provide. Add a route outside web middleware CSRF, and validate the X-Twilio-Signature header:

$validator = new \Twilio\Security\RequestValidator(config('services.twilio.token'));
$validator->validate($request->header('X-Twilio-Signature'), $request->fullUrl(), $request->post());

Do this before touching the payload, and make the handler idempotent on MessageSid (Twilio retries). If you are stuck on the pinned SDK 4.x, Twilio\Security\RequestValidator may not exist — fall back to Services_Twilio::validateRequest() or hand-roll the HMAC-SHA1 of URL + sorted params, which is ~10 lines.

Multi-tenant / subaccounts. The bundle is hardwired to a single SID — do not extend it. Instantiate new TwilioClient($subSid, $subToken) per tenant inside a resolver and never store those in the singleton.

Testing. Bind a fake behind your own SmsSender interface, or point the SDK at Twilio's test credentials. Http::fake() will not intercept the Twilio SDK — it uses its own Guzzle/PSR-18 client. Either inject a mock TwilioClient (it's a concrete class; wrap it in an interface first) or use the SDK's Twilio\Http\Client option to swap the transport.

Gotchas and Tips

  • Archived, latest is 1.0.2 (2016). This patch release changed nothing but a composer.json constraint: twilio/sdk: 4.*. It is a deliberate BC-break avoidance — the author pinned to 4.x rather than port to 5.x — which means the bundle is now frozen out of every SDK feature since 2015: Verify, Conversations, Messaging Services v2, A2P 10DLC helpers, and the v6 constructor changes. Treat it as documentation, not code.
  • The pin is a dependency-resolution trap. Because 1.0.2 hard-constrains 4.*, any app that also needs twilio/sdk ^5|^6|^7 gets an unresolvable conflict, and SDK 4.x's old Guzzle conflicts with Laravel's HTTP client stack. Do not try to --ignore-platform-reqs past this; you'll end up with two incompatible HTTP abstractions.
  • Service visibility. On Symfony 3.4+, services are private by default; $container->get('vresh_twilio') from a controller will throw. Inject by type/argument instead, and check debug:container --show-private.
  • Config key names drift. Read DependencyInjection/Configuration.php in the tagged release you pulled — sid/token vs account_sid/auth_token and whether from is even part of the tree has changed. A wrong key silently yields a client with null credentials and a confusing 401 at call time, not at boot.
  • No exception translation. You still catch Twilio\Exceptions\RestException (has getStatusCode() and getDetails()['code']) and TwilioException (transport/network). Map Twilio error codes to your own domain exceptions in SmsSender so the rest of the app never imports Twilio\.
  • Secrets. The bundle's documented flow is parameters.yml, which historically got committed to VCS. In Laravel keep SID/token in env() only, read through config(), and remember config:cache means env() outside config files returns null — a classic "works locally, broken in prod" here.
  • from matters. Use an E.164 number or a Messaging Service SID (MessagingServiceSid parameter — not available in the 4.x SDK the bundle pins) — trial accounts can only send to verified numbers, which is the #1 "it silently does nothing" report.
  • Localization/encoding. body over 160 GSM-7 chars is split and billed per segment; non-GSM characters (emoji, curly quotes) drop to 70 chars/segment. Compute segments before sending if cost matters.
  • Debugging. The bundle adds no logging. Enable the SDK's HTTP client logging, or in Laravel log $msg->sid + $msg->status on send and reconcile via the status callback. Without statusCallback you will never learn about undelivered/failed.
  • Escape hatch. There are no meaningful extension points (no events, no compiler passes worth using) — the only realistic extension is decorating the client service or wrapping it behind your own interface. Do the latter from day one; it makes the eventual removal of this bundle (or of Twilio itself) a one-file change.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource