Laravel already has the right tools
What tools does Laravel provide for sending SMS?
Laravel's built-in HTTP Client, Queued Jobs, and Webhooks are all you need to integrate SMSRoute's no-KYC SMS API. No extra packages required. With crypto billing and coverage across 149 countries, you can send your first message in minutes after signing up with just an email.
To send SMS with Laravel you don't need a package — the framework ships with everything the job requires. The HTTP client makes the API call. Queued jobs handle retries and burst control. Routes receive webhooks. Using them means your SMS layer behaves like the rest of a Laravel app — testable, queueable, observable — instead of a bolted-on integration. We target SMSRoute endpoints below (a no-KYC SMS API with crypto billing — BTC, ETH, USDT, XMR, LTC, and SOL — so first send is minutes), but the patterns transfer to any provider. SMSRoute's published route pages list delivery from $0.004/message (premium direct-carrier corridors up to $0.035) with sub-100ms median submission and ~98.6% delivered success (smsroute.cc route pages, 2026). For the authoritative reference, see the IETF idempotency-key draft.
| Laravel feature | SMS job it does | Why |
|---|---|---|
| HTTP client (Http::) | The send call | Built-in timeout, retry, and fake for tests |
| Queued jobs (ShouldQueue) | Retries + off-request sending | $tries/$backoff for free; never block a web request |
| Job rate limiting | Stay under provider throughput | Smooths bursts, avoids 429s and carrier flags |
| Routes (api.php) | DLR + inbound webhooks | Signature-verify then update idempotently |
| Http::fake() | Offline testing | Full coverage, zero per-commit spend |
// app/Services/Sms.php
use Illuminate\Support\Facades\Http;
class Sms
{
public function send(string $to, string $text): array
{
$res = Http::withToken(config('services.sms.key'))
->timeout(10) // never hang a request
->post('https://api.smsroute.cc/sms/send', [
'to' => $to, 'from' => 'YourSender', 'message' => $text,
]);
$res->throw(); // 4xx/5xx -> exception
return $res->json(); // generic response object
}
}
The key comes from config() (backed by an env var), never a literal. The integration-patterns discipline every language shares.
And ->timeout(10) is set, because an SMS send blocking a web request is a worse failure than a
clean exception. For example, set `SMS_ROUTE=log` in `.env` and add `'sms_route' => env('SMS_ROUTE')` to
`config/services.php`.
Queued jobs handle retries and bursts
How do queued jobs improve SMS delivery reliability?
Laravel's queued jobs automatically retry failed SMS sends and handle traffic bursts without blocking your app. Combined with SMSRoute's adaptive multi-route delivery and automatic failover, your messages reach 149 countries reliably. Failed messages are automatically credited back to your balance.
Don't send SMS inline in a controller. Dispatch a job: it moves the send off the request path, gives you Laravel's built-in retry and backoff, and lets the queue throttle bursts under the provider's throughput ceiling. This solves retries and rate limiting at once.
class SendSmsJob implements ShouldQueue
{
public int $tries = 3;
public array $backoff = [1, 2, 4]; // seconds, per attempt
public function __construct(public string $to, public string $text) {}
public function handle(Sms $sms): void
{
$sms->send($this->to, $this->text); // exception -> auto retry
}
public function retryUntil(): \DateTime
{
return now()->addSeconds(30); // cap total time for OTP
}
}
$tries+$backoffgive you the retry-with-backoff pattern for free. But note Laravel retries on any exception, so throw only on retriable failures (timeouts, 5xx), not on a 4xx you should fix.retryUntil()caps total time so an OTP send doesn't retry for minutes; past it, surface a resend option per the OTP UX ladder.- Throttle the queue worker to stay under provider throughput. Laravel's rate-limiting middleware on the job does this cleanly.
- Dispatch, don't await.
SendSmsJob::dispatch($to, $text)returns instantly; the user's request doesn't wait on the carrier.
DLR webhooks as routes
How do DLR webhooks work for SMS delivery tracking?
SMSRoute sends real-time delivery reports (DLR) to your Laravel webhook routes. Simply define a route in your web.php file to receive delivery status updates. This gives you instant visibility into message delivery across 149 countries, with automatic retries and refunds for any failures.
Delivery receipts arrive on a route you expose. Verify the signature, then update state idempotently. The DLR handling and webhook security patterns, expressed in Laravel.
// routes/api.php
Route::post('/sms/dlr', function (Request $r) {
// verify HMAC signature before trusting anything
if (! Sms::verify($r->getContent(), $r->header('X-Signature'))) {
abort(401);
}
$dlr = $r->json();
DeliveryStatus::updateOrCreate( // idempotent on message id
['message_id' => $dlr['id']],
['status' => $dlr['status']]
);
return response()->noContent(); // ack fast
});
updateOrCreate keyed on the message id gives you idempotency for free. Duplicate and
out-of-order DLRs (both normal) resolve to one row. Verify the signature *before* touching the database, so a
forged callback never reaches your logic.
Testing with Laravel's HTTP fake
How can I test SMS sending in Laravel without real messages?
Use Laravel's HTTP fake to intercept SMSRoute API calls during testing. Assert that the correct payload was sent to the right endpoint, then verify your queued jobs and webhook handlers work correctly. This lets you build and test your entire SMS workflow without spending any crypto.
Laravel's Http::fake() makes offline testing trivial. No per-commit spend, full coverage of the
send and retry paths.
Http::fake([
'smsroute.cc/*' => Http::response(['id' => 'test_123', 'status' => 'queued'], 200),
]);
(new Sms)->send('+15551234567', 'test');
Http::assertSent(fn ($req) => $req['to'] === '+15551234567');
Assert on response shape and that the right request went out. Fake a 500-then-200 sequence to prove the job retries once and succeeds. Use a fake signature to test the webhook route rejects forgeries. Keep one real send to your own phone as a pre-release smoke test. The full method is in the test-numbers guide. Wire E.164 normalization at your form-request layer, and the Laravel SMS integration is production-shaped end to end.
Related reading
FAQ
How do I send an SMS in Laravel?
Should I use a queued job for sending SMS in Laravel?
How do I handle SMS delivery webhooks in Laravel?
How do I test Laravel SMS code without sending real messages?
Send your first SMS in 5 minutes
No KYC. Pay with BTC, ETH, USDT, XMR, LTC, and SOL. Live routes to 149 countries.
Get an API key →