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

Aws Sns Laravel Package

laravel-notification-channels/aws-sns

Send Laravel notifications through AWS SNS. This package adds an SNS notification channel, letting you deliver messages to SMS, email, and other SNS-supported endpoints via the AWS SDK. Simple setup and seamless integration with Laravel's notification system.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Check your Laravel floor before anything else. v2.0.0 requires laravel/framework >= 12.11.0, because the channel no longer catches exceptions or fires its own NotificationFailed — it relies on Laravel's framework-level dispatch introduced in that release. Composer will refuse to resolve below that line. Decide deliberately: bump Laravel as its own PR, or pin ^1.0 and record it as dated technical debt. Don't try to force resolution.

composer require laravel-notification-channels/aws-sns

2. Publish the config and read it end to end. This is where you learn which env keys the package actually reads and whether credentials come from config or fall through to the AWS SDK's default chain.

php artisan vendor:publish --provider="NotificationChannels\AwsSns\SnsServiceProvider"

Check the published config/sns.php and the SNS block in config/services.php (key, secret, region, default topic ARN). On EC2/ECS/EKS/Vapor, leave key/secret empty and use IAM roles — empty strings in config can shadow the SDK's credential discovery and produce confusing "no credentials" errors.

3. Grant minimum IAM. sns:Publish scoped to the topic ARNs you use, plus SMS send permissions for direct sends. Prove credentials and permissions independently with one publish from the AWS console before you debug package wiring.

4. Ship one SMS end to end. The whole happy path is three small pieces: a via() that names the channel, a toSns() that returns a message, and a route on the notifiable.

class OrderShipped extends Notification implements ShouldQueue
{
    public function via(object $notifiable): array
    {
        return [SnsChannel::class];
    }

    public function toSns(object $notifiable): SnsMessage
    {
        return SnsMessage::create()
            ->message("Order #{$this->order->id} has shipped.");
    }
}
public function routeNotificationForSns(): string
{
    return $this->phone; // E.164: +15551234567
}

That's your first use case: transactional SMS through the notification pipeline, queued, with zero AWS SDK code in your app layer. The package is small — after this works, read SnsChannel and SnsMessage directly (10 minutes) rather than hunting docs; the surface area is genuinely that thin.

Implementation Patterns

One message class, three addressing modes. Don't write separate notification classes for SMS, topics, and push endpoints. Resolve the target from the notifiable and set it on the message:

SnsMessage::create()->message($body)->phoneNumber($notifiable->phone);
SnsMessage::create()->message($body)->topicArn($notifiable->topic_arn);
SnsMessage::create()->message($body)->endpointArn($notifiable->endpoint_arn);

The clean separation: routeNotificationForSns() returns the address (phone or ARN), toSns() builds the body and stays addressing-agnostic. Same notification class then serves both user-facing sends and broadcast/ops alerts.

On-demand recipients. For numbers that aren't models (support escalations, one-off paging, a phone the user just typed in), use framework routing rather than inventing a throwaway notifiable:

Notification::route('sns', $e164)->notify(new ServiceDegraded($incident));

Attributes are your only per-message analytics hook. SNS gives you nothing back through this channel, so push a string attribute for correlation and query it later in CloudWatch:

SnsMessage::create()
    ->message($body)
    ->attributes(['tenant_id' => (string) $tenant->id, 'kind' => 'billing']);

Because each notification will otherwise invent its own body/attribute conventions, build a tiny app-level factory — one static method per message category (SnsMessages::orderShipped($order)) — so formatting, sender ID, and attribute keys stay consistent across the codebase.

Handle failure once, at the framework event. With 2.x the canonical hook is NotificationFailed, registered once in a service provider:

Event::listen(function (NotificationFailed $event) {
    if ($event->channel !== SnsChannel::class) {
        return;
    }

    report(new SnsDeliveryFailed($event->notifiable, $event->notification, $event->data));
});

Map the package's distinct exception classes to retry UX here: invalid recipient → don't retry, prompt the user to fix their number; transport/provider failure → retry with backoff. Without that split every failure collapses into one unhelpful state that gets escalated to engineering.

Tame the queue. Failures now propagate, so queued sends retry — and SNS bills per message. Set explicit $tries and backoff() on ShouldQueue notifications, and add ShouldBeUnique when a duplicate SMS is worse than a missed one. Always queue SMS sends; doing them inline adds AWS round-trip latency to a user request.

Keep local/CI sends off AWS. Have via() return ['log'] (or Notification::fake()) outside production, or gate on config. The channel has no dry-run mode.

Test the payload, not the SDK. In application tests, Notification::fake() plus assertions on toSns() output covers your message logic completely:

Notification::fake();
Notification::route('sns', '+15551234567')->notify(new ServiceDegraded($incident));
Notification::assertSentOnDemand(ServiceDegraded::class);

Only stub the Aws\Sns\SnsClient when you're specifically testing the channel's failure path — and then assert exactly one NotificationFailed dispatch per failed send.

Gotchas and Tips

The 1.x → 2.x upgrade fails open, which is the dangerous part. Old catch blocks and old exception imports don't error — they simply stop matching, so your fallback silently never runs and nothing logs. Before bumping, grep for prior exception FQCNs, NotificationFailed listeners, and every try/catch around Notification::send(). That grep result is your acceptance checklist.

Failures that used to be absorbed now surface — including in failed_jobs. A retry storm on billable SMS is a spend event, not just an availability event. Watch SNS spend for the first full cycle after deploy; add an alert on failed-job rate for queued notifications.

SNS SMS sandbox mode is the #1 "works on my machine" trap. New AWS accounts can only message verified numbers until you exit the sandbox and raise the spending quota. A staging send to your own phone proves nothing about production. Check the spending limit and per-country origination settings early — that's a ticket to AWS support, so start it before you need it.

Phone numbers must be E.164. 555-1234 or a bare 10-digit string fails at the provider as an invalid parameter. Normalize on write (in your model/mutator or form request), not in the notification — you want bad data visible at the source, not as a 2 a.m. delivery failure.

Empty-string credentials shadow the SDK chain. If you're on IAM roles, confirm the config isn't passing '' for key/secret. And if credentials do live in config, php artisan config:cache means rotation requires a re-cache and redeploy.

Region and ARN must agree. An ARN from us-east-1 published via a client configured for eu-west-1 fails in a way that reads like a permissions error. If you support multiple regions, assert the pairing in a boot-time check or a test.

You get no delivery receipts. SNS can log delivery status to CloudWatch, but that's configuration-set work on the SNS side, not something this channel surfaces. If the product needs "did the customer receive it," budget for CloudWatch log parsing as a separate task — don't assume the package closes that gap.

Watch size on both axes. 256KB SNS limit includes attributes, and SMS bodies over the segment threshold split and bill multiple times. Trim bodies, prefer short links, and don't stuff base64 into an attribute.

Debugging order when a send silently does nothing: (1) is the notification queued and is a worker actually running; (2) does via() include SnsChannel::class as a container-resolvable string; (3) does routeNotificationForSns() return a non-null E.164 number or ARN; (4) did a NotificationFailed listener swallow it; (5) only then go to AWS credentials and region. Most "the package is broken" reports die at step 3.

Extension point: the channel is small enough to subclass or vendor outright. For request/response logging, metrics, or a custom transport wrapper, decorate the underlying Aws\Sns\SnsClient and bind your instance in the container rather than forking. And if the package goes quiet (56 stars, single maintainer, MIT), that same thinness is what turns a fork into a day of work instead of a project — but don't put it on a customer-facing send path without accepting that risk explicitly.

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.
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
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager