PHPackages                             mysteryinfosolutions/crazytel-sms - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. mysteryinfosolutions/crazytel-sms

ActiveLibrary

mysteryinfosolutions/crazytel-sms
=================================

Laravel package for sending SMS via the Crazytel API (https://developer.crazytel.io)

v1.0.0(1mo ago)05MITPHPPHP ^8.1

Since Jul 12Pushed 1mo agoCompare

[ Source](https://github.com/mysteryinfosolutions/crazytel-sms)[ Packagist](https://packagist.org/packages/mysteryinfosolutions/crazytel-sms)[ Docs](https://github.com/mysteryinfosolutions/crazytel-sms)[ RSS](/packages/mysteryinfosolutions-crazytel-sms/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (2)Versions (2)Used By (0)

Crazytel SMS for Laravel
========================

[](#crazytel-sms-for-laravel)

A small Laravel package for sending SMS through the [Crazytel API](https://developer.crazytel.io)(`POST /api/v2/sms/send` and `POST /api/v2/sms/bulk-send`), plus account balance, phone numbers, verified caller IDs, and SMS delivery tracking.

Install
-------

[](#install)

If you're publishing this as a local/private package, add it to your app's `composer.json`as a path repository, or just copy the `src/`, `config/`, and `composer.json` into a `packages/crazytel-sms` directory in your Laravel app. Then:

```
composer require mysteryinfosolutions/crazytel-sms:@dev
php artisan vendor:publish --tag=crazytel-sms-config
```

This publishes `config/crazytel-sms.php`.

Configure
---------

[](#configure)

Add to your `.env`:

```
CRAZYTEL_API_KEY=your_sms_api_key_from_the_crazytel_portal
CRAZYTEL_SMS_FROM=61412345678

```

Notes:

- The SMS API key is generated separately in the Crazytel portal (not the same as your general account API key).
- `from` must be a verified Caller ID / SMS sender number starting with `04` or `614`.
- `CRAZYTEL_BASE_URL` defaults to `https://crazytel.io` — override only if Crazytel gives you a different base URL for your account.

Usage
-----

[](#usage)

### Facade

[](#facade)

```
use Crazytel\Sms\Facades\CrazytelSms;

// Uses the default "from" number from config
$result = CrazytelSms::send('0412345678', 'Your OTP is 123456');

if ($result->success) {
    // Keep $result->uuid to look up delivery status/events later
    logger()->info("SMS {$result->status}, uuid {$result->uuid}, cost {$result->price} {$result->priceUnit}");
}

// Override the sender per-call
CrazytelSms::send('61412345678', 'Hello!', from: '61498765432');

// Optional: suppression list + an idempotency key so a retry can't double-send
CrazytelSms::send('0412345678', 'Hello!', optOutList: 'marketing', idempotencyKey: 'order-42-otp');
```

Sends go through the current **`POST /api/v2/sms/send`** endpoint. The `$result->uuid`is the message's stable ID for later delivery-tracking lookups.

### Dependency injection

[](#dependency-injection)

```
use Crazytel\Sms\CrazytelSms;

class ReminderController
{
    public function __invoke(CrazytelSms $sms)
    {
        $sms->send('0412345678', 'Your appointment is tomorrow at 10am.');
    }
}
```

### Bulk send

[](#bulk-send)

```
use Crazytel\Sms\Facades\CrazytelSms;

$batch = CrazytelSms::sendBulk(
    to: ['0412345678', '0498765432'],
    message: 'System maintenance tonight 11pm-1am AEST.',
    optOutList: 'announcements', // optional, recommended for campaigns
);

// Batch summary
echo "{$batch->queued} queued, {$batch->rejected} rejected, {$batch->skipped} skipped";

// Per-recipient results — the result is iterable and countable
foreach ($batch as $result) {
    // $result->to, $result->success, $result->status, $result->uuid, $result->statusDetail
}
```

`sendBulk()` returns a `Crazytel\Sms\Messages\BulkSmsResult` (from `POST /api/v2/sms/bulk-send`): batch counters as properties, plus the per-recipient `SmsResult` list you can iterate directly.

### Error handling

[](#error-handling)

Both `send()` and `sendBulk()` throw `Crazytel\Sms\Exceptions\CrazytelSmsException` on HTTP failure (400 malformed body, 401 unauthorized, 402 insufficient balance, 403 sender not allowed, 422 invalid SMS data, 429 rate limited, 500 server error, or 502 SMS service unreachable), and also for invalid phone number formats.

```
use Crazytel\Sms\Exceptions\CrazytelSmsException;

try {
    CrazytelSms::send($to, $message);
} catch (CrazytelSmsException $e) {
    logger()->error($e->getMessage(), $e->context());
}
```

### Laravel Notifications

[](#laravel-notifications)

You can also route notifications through Crazytel:

```
use Crazytel\Sms\Channels\CrazytelSmsChannel;
use Crazytel\Sms\Messages\CrazytelSmsMessage;
use Illuminate\Notifications\Notification;

class AppointmentReminder extends Notification
{
    public function via($notifiable): array
    {
        return [CrazytelSmsChannel::class];
    }

    public function toCrazytelSms($notifiable): CrazytelSmsMessage
    {
        return CrazytelSmsMessage::create("Reminder: your appointment is at {$this->time}.");
    }
}
```

On your notifiable model (e.g. `User`), define where the SMS should go:

```
public function routeNotificationForCrazytelSms(): string
{
    return $this->phone_number;
}
```

Then register the channel in a service provider if you want to resolve it via the container automatically — Laravel will resolve `CrazytelSmsChannel` from the container, which in turn resolves `CrazytelSms` (already bound by `CrazytelSmsServiceProvider`).

Account resources
-----------------

[](#account-resources)

Besides sending SMS, the `Crazytel` client exposes a few read-only account endpoints. Resolve it via the `Crazytel` facade, dependency injection, or `app(\Crazytel\Sms\Crazytel::class)`.

```
use Crazytel\Sms\Facades\Crazytel;

// Credit balance
$balance = Crazytel::balance()->get();
echo $balance->balance;          // e.g. 42.50
echo $balance->accountCode;

// Your DIDs (optionally filtered by country/state/city/number_type/did_number)
foreach (Crazytel::phoneNumbers()->list() as $number) {
    echo "{$number->didNumber} ({$number->state}) \${$number->monthlyFee}/mo\n";
}
$did = Crazytel::phoneNumbers()->find('61399999999'); // full details in $did->raw

// Verified Caller IDs — validate a "from" before you send
foreach (Crazytel::callerIds()->verified() as $cli) {
    echo "{$cli->number} ({$cli->nickname})\n";
}
if (! Crazytel::callerIds()->isVerified('0412345678')) {
    // don't attempt the send — it would 422
}

// SMS delivered/failed reporting for a window (defaults: last 30 days)
$summary = Crazytel::smsCenter()->summary(from: now()->subDays(7), to: now());
echo "delivered: {$summary->outboundDelivered}, failed: {$summary->outboundFailed}\n";
echo "cost: \$" . $summary->totalCostDollars();
```

`balance()`, `phoneNumbers()`, `callerIds()`, and `smsCenter()` throw `Crazytel\Sms\Exceptions\CrazytelException` on HTTP failure (SMS *sending* still throws the more specific `CrazytelSmsException`, which extends it).

### Delivery tracking

[](#delivery-tracking)

`send()`/`sendBulk()` return a `uuid` per message — use it to confirm a customer-care SMS actually reached the handset, not just that Crazytel accepted it:

```
$result = CrazytelSms::send('0412345678', 'Your appointment is tomorrow at 10am.');

// Full message record (status, text, timestamps)
$message = Crazytel::smsCenter()->message($result->uuid);
echo $message->status; // e.g. "delivered"

// Delivery lifecycle events: accepted -> sent -> delivered (or failed)
foreach (Crazytel::smsCenter()->events($result->uuid) as $event) {
    echo "{$event->occurredAt}: {$event->type} — {$event->description}\n";
}

// Inbound-forward attempts (if this message was forwarded to a webhook/email/number)
foreach (Crazytel::smsCenter()->forwards($result->uuid) as $attempt) {
    echo "{$attempt->destinationType} -> {$attempt->destination}: {$attempt->status}\n";
}

// Email-to-SMS reply records, if applicable
foreach (Crazytel::smsCenter()->emailReplies($result->uuid) as $reply) {
    echo "{$reply->fromEmail}: {$reply->status}\n";
}
```

`events()`, `forwards()`, and `emailReplies()` are cursor-paginated — each returns a `CursorPage` (iterable/countable). Pass the page's `nextCursor` back in as `$cursor` to fetch the next page:

```
$page = Crazytel::smsCenter()->events($uuid, limit: 50);
while ($page->hasMore()) {
    $page = Crazytel::smsCenter()->events($uuid, cursor: $page->nextCursor, limit: 50);
}
```

The `Crazytel::sms()` resource is equivalent to the standalone `CrazytelSms`facade/class — both send through the same code.

Number format
-------------

[](#number-format)

Numbers must be Australian mobiles starting with `04` (e.g. `0412345678`) or `614`(e.g. `61412345678`). Crazytel converts these to E.164 internally.

API reference used
------------------

[](#api-reference-used)

- `POST /api/v2/sms/send`, `POST /api/v2/sms/bulk-send` (SMS — current V2 endpoints; the deprecated V1 `/api/v1/sms/*` endpoints are no longer used)
- `GET /api/v1/balance/` (balance)
- `GET /api/v1/phone-numbers`, `GET /api/v1/phone-numbers/{did_number}` (DIDs)
- `GET /api/v1/account/verified-cli/` (verified Caller IDs)
- `GET /api/v1/sms_center/stats/summary` (SMS reporting)
- `GET /api/v1/sms_center/stats/messages/{uuid}` (message detail)
- `GET /api/v1/sms_center/stats/messages/{uuid}/events` (delivery events)
- `GET /api/v1/sms_center/stats/messages/{uuid}/forwards` (forward attempts)
- `GET /api/v1/sms_center/stats/messages/{uuid}/email-replies` (email replies)
- Auth: header `X-Crazytel-Api-Key: your_api_key`
- Full spec:

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance90

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

50d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/47215871?v=4)[Mystery Info Solutions](/maintainers/mysteryinfosolutions)[@mysteryinfosolutions](https://github.com/mysteryinfosolutions)

---

Top Contributors

[![codesbytvt](https://avatars.githubusercontent.com/u/72726747?v=4)](https://github.com/codesbytvt "codesbytvt (2 commits)")

---

Tags

laravelsmsaustraliacrazytel

### Embed Badge

![Health badge](/badges/mysteryinfosolutions-crazytel-sms/health.svg)

```
[![Health](https://phpackages.com/badges/mysteryinfosolutions-crazytel-sms/health.svg)](https://phpackages.com/packages/mysteryinfosolutions-crazytel-sms)
```

###  Alternatives

[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k118.2M1.0k](/packages/laravel-socialite)[craftcms/cms

Craft CMS

3.6k3.7M3.5k](/packages/craftcms-cms)[laravel/boost

Laravel Boost accelerates AI-assisted development by providing the essential context and structure that AI needs to generate high-quality, Laravel-specific code.

3.6k31.1M882](/packages/laravel-boost)[spatie/laravel-health

Monitor the health of a Laravel application

89313.5M195](/packages/spatie-laravel-health)[illuminate/http

The Illuminate Http package.

13239.1M8.7k](/packages/illuminate-http)[nativephp/mobile

NativePHP for Mobile

1.2k128.7k171](/packages/nativephp-mobile)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
