sudiptpa / laravel-sent-dm
An expressive Laravel adapter for the Sent.dm unified messaging API: SMS, WhatsApp and RCS with a fluent, elegant interface.
Fund package maintenance!
Requires
- php: ^8.2
- illuminate/bus: ^11.0|^12.0|^13.0
- illuminate/cache: ^11.0|^12.0|^13.0
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/events: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/notifications: ^11.0|^12.0|^13.0
- illuminate/queue: ^11.0|^12.0|^13.0
- illuminate/routing: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- sentdm/sent-dm-php: ^0.32
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.0
- mockery/mockery: ^1.6
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.0
- phpstan/extension-installer: ^1.0
- phpstan/phpstan: ^2.0
- phpstan/phpstan-deprecation-rules: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-23 07:38:08 UTC
README
A Laravel package for Sent.dm, the unified messaging API for SMS, WhatsApp, and RCS.
This package wraps the official sentdm/sent-dm-php SDK with a full Laravel integration layer: queued sends, notification channels, webhook handling, message logging, opt-out management, multi-tenancy, and a complete testing suite. All HTTP transport goes through the official SDK, including sender profiles, channels, and compliance, which the SDK doesn't have typed methods for yet (see CONTRIBUTING.md). This package adds the Laravel idioms on top.
What this package handles
These things are wired up for you and work out of the box:
- Immediate or queued sends:
send()calls the API synchronously;sendLater()dispatches a Laravel job - Auto-channel routing: Sent.dm picks WhatsApp or SMS based on the recipient's reachability
- Webhook signature verification: HMAC-SHA256 checked at middleware level before your code runs
- Idempotent deduplication: webhook events are deduplicated so retried deliveries don't fire your listeners twice
- Rate limit handling: queued sends retry 429 responses using the API's
Retry-Afterdelay - Caching: contacts, templates, profiles, and number lookups are cached per-key with tag-based invalidation
- Multi-tenancy: same driver pattern as
MailandCache; switch accounts per request withSent::connection() - Organization profile scoping: scope any resource call to one child profile of an organization key with
->profile($id) - Message log: opt-in DB table that records successful queued sends and syncs delivery status from webhooks
- Opt-out compliance: STOP/UNSTOP keywords handled automatically; guard blocks sends to opted-out numbers
- Testing:
Sent::fake()with full assertions so you never make real API calls in tests
What stays in your application
These things belong in your app, not in the package:
- Deciding when to send a message: that's business logic
- Template content: created and managed in the Sent.dm dashboard
- Campaign scheduling: use Laravel's
schedule()to dispatch bulk sends on a cron - Analytics UI: build your own dashboard using
$user->sentMessages()data - Contact import: sync from your DB using
Sent::contacts()->create()in a job or command - Custom retry strategies: listen to
MessageFailedand re-dispatch with your own logic - Per-user notification preferences: check
$user->optedOutFromSent()before sending
Requirements
- PHP 8.2+
- Laravel 11, 12, or 13
Installation
composer require sudiptpa/laravel-sent-dm
Publish the config file:
php artisan sent:install
Add your API key to .env:
SENT_API_KEY=your-api-key
Verify the connection:
php artisan sent:health
Configuration
The published config is at config/sent.php:
'default' => env('SENT_CONNECTION', 'default'), 'connections' => [ 'default' => [ 'api_key' => env('SENT_API_KEY'), ], ], 'default_channel' => env('SENT_DEFAULT_CHANNEL'), // null = auto-route 'queue' => [ 'connection' => env('SENT_QUEUE_CONNECTION'), 'name' => env('SENT_QUEUE_NAME', 'default'), ], 'webhook' => [ 'enabled' => env('SENT_WEBHOOK_ENABLED', false), 'secret' => env('SENT_WEBHOOK_SECRET'), 'path' => env('SENT_WEBHOOK_PATH', 'sent/webhook'), ], 'cache' => [ 'enabled' => env('SENT_CACHE_ENABLED', true), 'ttl' => env('SENT_CACHE_TTL', 3600), ], 'sandbox' => env('SENT_SANDBOX', false), 'logging' => [ 'enabled' => env('SENT_LOGGING_ENABLED', false), ], 'opt_out' => [ 'enabled' => env('SENT_OPT_OUT_ENABLED', false), 'guard' => env('SENT_OPT_OUT_GUARD', false), 'keywords' => ['STOP', 'UNSUBSCRIBE', 'CANCEL', 'END', 'QUIT'], 'opt_in_keywords' => ['START', 'YES', 'UNSTOP'], ],
Quick start
use Sujip\SentDm\Facades\Sent; Sent::to('+61412345678') ->template('otp-verification') ->send();
That's an immediate, synchronous send. For a queued send, a plain-text body, multiple channels, template variables, and everything else, see Sending messages.
Documentation
| Guide | Covers |
|---|---|
| Sending messages | Immediate and queued sends, template variables, idempotency, bulk messaging, the notification channel |
| Sandbox mode | Simulating writes without real delivery, per-call and globally |
| Webhooks | Receiving delivery events, signature verification, managing endpoints from code |
| Message log | The opt-in sent_logs table, HasSentMessages, query scopes, status tracking |
| Opt-out management | STOP/START keyword handling, HasSentContact, the send guard |
| Multi-tenancy | Organization profile scoping and multiple Sent.dm connections |
| Number lookup and validation | Carrier lookup and the sentMobileNumber validation rule |
| API reference | Contacts, Templates, Profiles, Users, Messages, Conversations, Account, Artisan commands |
| Testing | Sent::fake() and its full assertion set |
Sponsoring
If this package has been useful to you, GitHub Sponsors is a simple way to support ongoing maintenance, improvements, and future releases.
Contributing
Contributions are welcome. Please open an issue to discuss what you'd like to change, or submit a pull request directly for bug fixes and small improvements. Make sure composer test, composer stan, and composer lint:check all pass before submitting.
License
This package is open source, licensed under the MIT license.