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 Laravel Package

aloha/twilio

Laravel Twilio integration for sending SMS and making calls via a simple facade or IoC-resolved manager. Supports multiple config connections and sender numbers, plus artisan commands and a publishable config file.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up

composer require aloha/twilio
php artisan vendor:publish --provider="Aloha\Twilio\Support\Laravel\ServiceProvider"

The package is a thin wrapper around the official twilio/sdk, so the composer install will pull that in as a transitive dependency. Before you commit, run composer why twilio/sdk and confirm the resolved SDK version satisfies your PHP version — this is the single most common install failure on this package (see Gotchas).

Configure credentials

Everything lives in config/twilio.php. Set it up from env so it survives deployments:

// config/twilio.php
return [
    'default' => env('TWILIO_CONNECTION', 'main'),
    'connections' => [
        'main' => [
            'sid'   => env('TWILIO_SID'),
            'token' => env('TWILIO_TOKEN'),
            'from'  => env('TWILIO_FROM'), // E.164, e.g. +15551234567
        ],
    ],
];

Add the facade alias to config/app.php (the README predates package auto-discovery, so do this manually even if the provider does register itself):

'aliases' => [
    // ...
    'Twilio' => Aloha\Twilio\Support\Laravel\Facade::class,
],

First use case: smoke-test with the shipped commands

Don't write code first. The package registers two artisan commands that resolve the manager from the container and exercise your credentials end to end:

php artisan twilio:sms +15555550123 "hello from laravel"
php artisan twilio:call +15555550123

If twilio:sms succeeds your SID, token, from-number and outbound permissions are all correct, and every later bug is your code rather than your config. If it fails with a 21608, you're on a trial account and the destination isn't verified — fix that before looking anywhere else.

Then in application code

use Twilio;

Twilio::message('+15555550123', 'Your order has shipped.');

Under the hood that's the Aloha\Twilio\Twilio object for the default connection, reachable at any time via app('twilio')->connection().

Implementation Patterns

Inject the interface, not the facade. The service provider binds Aloha\Twilio\TwilioInterface to the default connection. That gives you constructor injection, which is testable and keeps facades out of your domain code:

final class SmsNotifier
{
    public function __construct(private \Aloha\Twilio\TwilioInterface $twilio) {}

    public function notify(string $e164, string $body): void
    {
        $this->twilio->message($e164, $body);
    }
}

Note the binding is to the default connection only. The moment you need per-tenant or per-country numbers you have to inject Aloha\Twilio\Manager and call $manager->connection('uk') yourself.

Multi-connection by region or tenant. Define one connection per sending identity in config/twilio.php, then select at runtime. This is the cleanest way to model things like a US number for domestic traffic and a local number for EU traffic, because Twilio's deliverability and long-code throughput limits are per-sender.

Queue the send. Every call is a blocking HTTP round trip to Twilio, typically 150–600 ms, and a slow Twilio response will otherwise show up in your p95. Wrap the send in a queued job with a retry policy, and never fire it inside a DB transaction — you risk sending a duplicate SMS when the transaction rolls back after the API call already succeeded.

Custom notification channel. Rather than sprinkling facade calls around, implement toTwilio() on your notification classes and register a channel that resolves TwilioInterface out of the container. You keep Laravel's standard queueing/retry semantics and your notification class stays a plain DTO of content.

Inbound webhooks. Sending is the easy half. For inbound SMS/voice, accept the webhook on a route, validate the X-Twilio-Signature header, and keep the handler idempotent — Twilio retries non-2xx webhook responses. The package does not help you here; you're back on the raw SDK or manual signature validation.

Testing. Because the contract is bound in the container, $this->mock(TwilioInterface::class) in a feature test lets you assert messages were sent without touching the network. That's the main argument for injecting the interface over using the facade everywhere.

Gotchas and Tips

The package is effectively frozen. Last tagged release was July 2021. Its composer constraint on twilio/sdk is old, and on PHP 8.2+/Laravel 10+ you will likely hit a dependency conflict at install time. Mitigation: composer require aloha/twilio twilio/sdk:^8.0 --with-dependencies and verify the wrapper's method signatures still line up with the SDK version you resolved. If they don't, the wrapper is ~200 lines — read Manager and Twilio, then decide whether to skip it and use twilio/sdk directly. For a new project, seriously consider going straight to the official SDK; the value here is the config file and the two artisan commands, not the abstraction.

Facade alias 'twilio' is not the Twilio client. The container alias twilio resolves Aloha\Twilio\Manager; the static Twilio facade resolves the default connection. Mixing them up produces confusing "method does not exist" errors when you call a send method on the manager. Rule of thumb: Manager has connection() and getDefaultConnection(); the connection object has message() and call().

config:cache will bite you. Every env change to TWILIO_FROM etc. requires php artisan config:cache on production. A very common self-inflicted incident is rotating a token in .env, forgetting the recache, and seeing auth failures with the old SID in the logs.

Numbers must be E.164. +15551234567, no dashes, no parentheses, no leading zeroes on international numbers. from in config is also E.164 and must be a number owned by that account — copying a SID/token pair across subaccounts but keeping the old from yields a 21606.

Trial account limits are the number-one support ticket. Verified numbers only (error 21608), and the message body is prefixed with the Twilio trial banner. Don't debug your code before you've checked the account's verification state.

Set ssl_verify off only locally. The config exposes an SSL verify flag; people disable it to get around corporate MITM proxies and then ship it to production. Keep it true in config/twilio.php and override only in .env.

Error handling. Wrap in try/catch (Twilio\Exceptions\RestException $e) and branch on $e->getStatusCode() / $e->getCode() — transient failures (429, 5xx) deserve a retry with backoff, while 21211 (invalid to) and 21610 (recipient unsubscribed) are permanent and should not be retried. Without this distinction your queue fills with poison messages.

Throughput. US/Canada long codes are limited to roughly 1 message per second per number and can be filtered by carriers. If you have any real volume, move from to a Messaging Service SID rather than a raw number — you forfeit the package's from-based convenience here and pass the service SID explicitly.

Never log the token. If you dump config for debugging, config('twilio') will print both the SID and auth token into your logs. Redact before dumping, and keep the token out of exception messages.

Debugging tip. When a send silently does nothing, check the Twilio console's debugger first — the package throws on API errors, but misroutes and carrier filtering appear there and nowhere in your logs.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo