PHPackages                             mg-techlegend/laravel-notify-africa - 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. [Mail &amp; Notifications](/categories/mail)
4. /
5. mg-techlegend/laravel-notify-africa

ActiveLibrary[Mail &amp; Notifications](/categories/mail)

mg-techlegend/laravel-notify-africa
===================================

Laravel Notify Africa is a lightweight Laravel package for sending SMS via the Notify Africa API. It provides a clean, expressive interface for single and bulk messaging, integrates with Laravel Notifications, and simplifies SMS delivery without dealing with raw HTTP requests.

v1.0.0(3mo ago)18MITPHPPHP ^8.4CI passing

Since Mar 21Pushed 1w agoCompare

[ Source](https://github.com/mg-techlegend/laravel-notify-africa)[ Packagist](https://packagist.org/packages/mg-techlegend/laravel-notify-africa)[ Docs](https://github.com/mg-techlegend/laravel-notify-africa)[ GitHub Sponsors](https://github.com/TechLegend)[ RSS](/packages/mg-techlegend-laravel-notify-africa/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (15)Versions (4)Used By (0)

Laravel Notify Africa
=====================

[](#laravel-notify-africa)

[![Latest Version on Packagist](https://camo.githubusercontent.com/4ef9ab12e2a5eb3189e0d129405d24f264554b48fd4433a6f79a4d3dbf6192fe/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d672d746563686c6567656e642f6c61726176656c2d6e6f746966792d6166726963612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/mg-techlegend/laravel-notify-africa)[![GitHub Tests Action Status](https://camo.githubusercontent.com/798e08420bb4a08a15c97104816e53bb27e91e78b4bdec70933d28d14d652620/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6d672d746563686c6567656e642f6c61726176656c2d6e6f746966792d6166726963612f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/mg-techlegend/laravel-notify-africa/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/3fb60cb8a879e9e4a65b151110de363874ce40bad67a4ec51dd1cc2508bf3ec1/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6d672d746563686c6567656e642f6c61726176656c2d6e6f746966792d6166726963612f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/mg-techlegend/laravel-notify-africa/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/1708849a3627f8c560cc87bda0432a8de75b3df8e4c80e0e5719405df3ae8fe4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d672d746563686c6567656e642f6c61726176656c2d6e6f746966792d6166726963612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/mg-techlegend/laravel-notify-africa)

Send SMS through the [Notify Africa](https://notify.africa/) HTTP API from Laravel apps. This package provides a small typed client, a fluent message builder, structured response objects, Laravel Notification channel support, and a facade—using Laravel’s HTTP client (no direct Guzzle usage in your code).

Installation
------------

[](#installation)

```
composer require mg-techlegend/laravel-notify-africa
```

Publish the configuration file:

```
php artisan vendor:publish --tag="laravel-notify-africa-config"
```

Laravel discovers the service provider and facade automatically from `composer.json` (`extra.laravel.providers` and `extra.laravel.aliases`). No manual registration is required in typical apps.

Set the following in your `.env` (values are read **only** through `config/notify-africa.php`):

VariableDescription`NOTIFY_AFRICA_API_TOKEN`Bearer API token from Notify Africa`NOTIFY_AFRICA_SENDER_ID`Default sender ID (can be overridden per message)`NOTIFY_AFRICA_BASE_URL`Optional; default `https://api.notify.africa``NOTIFY_AFRICA_TIMEOUT`Request timeout in seconds (default `10`)`NOTIFY_AFRICA_CONNECT_TIMEOUT`Connect timeout in seconds (default `5`)`NOTIFY_AFRICA_HTTP_RETRY_ATTEMPTS`Total HTTP attempts per call (default `1` = no retries); see [HTTP retries](#http-retries)`NOTIFY_AFRICA_HTTP_RETRY_DELAY_MS`Delay in milliseconds between retries (default `250`)`NOTIFY_AFRICA_DEFAULT_COUNTRY_CODE`Optional; see [Phone numbers](#phone-numbers)`NOTIFY_WABA_API_KEY`WhatsApp (WABA) API key — **separate** credential from SMS; see [WhatsApp (WABA)](#whatsapp-waba)`NOTIFY_WABA_BASE_URL`Optional; default `https://notify-web-assistant-api.beagile.africa``NOTIFY_WABA_TIMEOUT`WABA request timeout in seconds (default `10`)`NOTIFY_WABA_CONNECT_TIMEOUT`WABA connect timeout in seconds (default `5`)`NOTIFY_WABA_WEBHOOK_SECRET`Optional; HMAC-SHA256 secret for verifying inbound WABA webhooks`NOTIFY_WABA_SIGNATURE_HEADER`Optional; signature header name (default `X-Notify-Signature`)Configuration
-------------

[](#configuration)

Published config: `config/notify-africa.php`.

- **`api_token`** — required for real API calls (missing token throws when the client is built).
- **`sender_id`** — default sender; omit on the message object to use this value.
- **`http_retry_attempts`** / **`http_retry_delay_ms`** — optional resilient requests; see below.
- **`default_country_calling_code`** — digits only, no `+` (e.g. `255`). Used only for “local-looking” numbers; see below.

HTTP retries
------------

[](#http-retries)

When `http_retry_attempts` is greater than `1`, the client retries **only** on connection failures and on HTTP **408**, **425**, **429**, and **5xx** responses. **4xx** errors such as **401** and **422** are not retried.

Retries use Laravel’s HTTP client (`throw: false` on the pending request) so the last response is always parsed and mapped to the same package exceptions. Increasing retries can mean duplicate SMS if a request succeeds at the gateway but the response never reaches your server—keep attempts conservative unless you accept that trade-off.

Direct usage
------------

[](#direct-usage)

Inject the entry service or use the facade:

```
use TechLegend\LaravelNotifyAfrica\Facades\LaravelNotifyAfrica;
use TechLegend\LaravelNotifyAfrica\LaravelNotifyAfrica as NotifyAfrica;
use TechLegend\LaravelNotifyAfrica\NotifyAfricaMessage;

// Facade
$response = LaravelNotifyAfrica::sendSms(
    LaravelNotifyAfrica::message()
        ->to('255689737459')
        ->content('Hello from Laravel!')
        // ->senderId('CUSTOM') // optional override
);

// Container (class or string alias registered by the package)
$notify = app(NotifyAfrica::class);
// $notify = app('notify-africa');

$response = $notify->sendSms(
    NotifyAfricaMessage::make()
        ->to('255689737459')
        ->content('Hello!')
);
```

`$response` is a `SendSmsResponse` with `messageId`, `deliveryStatus` (e.g. `PROCESSING`), and envelope metadata.

### Bulk SMS

[](#bulk-sms)

Uses the documented batch endpoint `POST /api/v1/api/messages/batch` (not a client-side loop):

```
use TechLegend\LaravelNotifyAfrica\Facades\LaravelNotifyAfrica;

$response = LaravelNotifyAfrica::sendBulkSms(
    ['255763765548', '255689737839'],
    'Same text for everyone',
    // optional third argument: sender ID override; otherwise config default is used
);

// $response->messageCount, creditsDeducted, remainingBalance
```

### Delivery status

[](#delivery-status)

```
$status = LaravelNotifyAfrica::getMessageStatus('156022');
// $status->status, $status->deliveredAt, etc.
```

WhatsApp (WABA)
---------------

[](#whatsapp-waba)

WhatsApp Business messaging uses its **own** base URL and API key — a different host and credential from SMS. Set `NOTIFY_WABA_API_KEY` (and, if needed, `NOTIFY_WABA_BASE_URL`). The HTTP retry and default country calling code settings are shared with the SMS client.

The WhatsApp service is resolved lazily, so SMS-only apps never need WABA credentials configured until they call `whatsapp()`.

```
use TechLegend\LaravelNotifyAfrica\Facades\LaravelNotifyAfrica;

// Plain text
LaravelNotifyAfrica::whatsapp()->sendText('255700000001', 'Habari! Karibu BrightSmile.');

// Pre-approved template (parameters keyed by position)
LaravelNotifyAfrica::whatsapp()->sendTemplate('255700000001', 'hello_world', [
    '1' => 'John',
    '2' => 'BrightSmile',
]);

// Multiple recipients (string or array; numbers are normalised like SMS)
LaravelNotifyAfrica::whatsapp()->sendText(['255700000001', '255700000002'], 'Hi');
```

`sendText` posts to `POST /v1/waba-api/messages/text` and `sendTemplate` to `POST /v1/waba-api/messages/template`, both with `Authorization: Bearer {NOTIFY_WABA_API_KEY}`. Both return a `WhatsAppSendResponse` with `apiStatus`, `envelopeMessage`, and a `results` array of `WhatsAppRecipientResult` (`to`, `success`, `messageId`, `error`). Failures map to the same exceptions as SMS (see [Exceptions](#exceptions)).

### Inbound and delivery webhooks

[](#inbound-and-delivery-webhooks)

Register your webhook URL in the Notify Portal, then handle inbound messages and delivery reports with `WabaWebhookHandler`:

```
use Illuminate\Http\Request;
use TechLegend\LaravelNotifyAfrica\Waba\WabaWebhookHandler;

Route::post('/webhooks/waba', function (Request $request, WabaWebhookHandler $handler) {
    $result = $handler->handle($request);
    // ['successful' => bool, 'event_type' => string, 'data' => [...], 'message' => string, 'status_code' => int]

    return response()->json($result, $result['status_code']);
});
```

When `NOTIFY_WABA_WEBHOOK_SECRET` is set, the handler verifies an HMAC-SHA256 signature from the configured header (`NOTIFY_WABA_SIGNATURE_HEADER`, default `X-Notify-Signature`); a bad or missing signature yields a `401`-shaped result. The inbound payload schema is undocumented, so every request is logged in full (via `Log::info`) and parsed defensively into normalised fields `from`, `text`, `wa_message_id`, `business_number`, and `event_type` (with the raw payload kept under `raw`). Confirm the real field names from the first logged delivery and adjust if needed.

Exceptions
----------

[](#exceptions)

ExceptionWhen`NotifyAfricaAuthenticationException`HTTP 401 / 403`NotifyAfricaValidationException`HTTP 400 / 422, or JSON envelope `status` ≠ 200 with HTTP 200`NotifyAfricaRequestException`Other failures, non-JSON responses, 5xx, connection issuesAll extend `NotifyAfricaException` and expose `?array $payload` with the decoded JSON when available.

Local validation (empty phone, empty message, missing sender, invalid notification setup) throws `InvalidArgumentException` before any HTTP call. Many messages are prefixed with `[Notify Africa]` so logs are easy to filter.

Laravel notifications
---------------------

[](#laravel-notifications)

Use the channel class in `via()` and implement `toNotifyAfrica()` on your notification. The notifiable must define `routeNotificationForNotifyAfrica()` returning the recipient number (string).

```
use Illuminate\Notifications\Notification;
use TechLegend\LaravelNotifyAfrica\Channels\NotifyAfricaChannel;
use TechLegend\LaravelNotifyAfrica\NotifyAfricaMessage;

class OrderShippedSms extends Notification
{
    public function via(object $notifiable): array
    {
        return [NotifyAfricaChannel::class];
    }

    public function toNotifyAfrica(object $notifiable): NotifyAfricaMessage
    {
        return NotifyAfricaMessage::make()
            ->content('Your order has shipped.');
        // Phone comes from routeNotificationForNotifyAfrica() when `to()` is omitted
    }
}
```

On your notifiable (e.g. `User` model):

```
public function routeNotificationForNotifyAfrica(): string
{
    return $this->phone; // e.g. 2557… (see below)
}
```

If `toNotifyAfrica()` already sets `->to(...)`, that number is used; otherwise the channel applies the routed number.

Phone numbers
-------------

[](#phone-numbers)

The API expects international format **without** a leading `+` (e.g. `255XXXXXXXXX`). The package strips spaces and non-digits and removes a leading `+`.

If you set `default_country_calling_code` (e.g. `255`) and the number looks “local” (9–10 digits after normalization), that prefix is prepended. This is a simple heuristic—not a substitute for [libphonenumber](https://github.com/giggsey/libphonenumber-for-php) or full validation. Prefer passing fully qualified international numbers in production.

Testing your app
----------------

[](#testing-your-app)

In tests, use Laravel’s HTTP client fakes so no real SMS is sent:

```
use Illuminate\Support\Facades\Http;

Http::fake([
    'https://api.notify.africa/api/v1/api/messages/send' => Http::response([
        'status' => 200,
        'message' => 'SMS sent successfully',
        'data' => ['messageId' => '1', 'status' => 'PROCESSING'],
    ], 200),
]);
```

If you change `notify-africa` config inside a test, clear resolved singletons or boot a fresh application before resolving `NotifyAfricaClient`, since it is registered as a singleton.

Testing this package
--------------------

[](#testing-this-package)

```
composer test
composer analyse   # PHPStan
composer format    # Pint
```

Assumptions (v1)
----------------

[](#assumptions-v1)

- Error semantics follow common HTTP usage (401/403 auth, 400/422 validation). If the live API differs, adjust mapping in `NotifyAfricaClient::mapFailure()` and keep tests in sync.
- Bulk sending uses the official batch API; behavior matches [Notify Africa SMS API](https://docs.notify.africa/docs/api/sms).
- No database tables, queues, webhooks, or logging persistence in v1.

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md).

Contributing
------------

[](#contributing)

See [CONTRIBUTING.md](CONTRIBUTING.md).

Security
--------

[](#security)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Thomson Maguru](https://github.com/mg-techlegend)
- [All Contributors](../../contributors)

### Notify Africa and iPF Softwares

[](#notify-africa-and-ipf-softwares)

The **SMS API** behind this package is **[Notify Africa](https://notify.africa/)**, built and operated by **[iPF Softwares](https://github.com/iPFSoftwares)**. Documentation: [SMS API](https://docs.notify.africa/docs/api/sms). Official PHP client (separate from this Laravel package): [notify-africa-php](https://github.com/iPFSoftwares/notify-africa-php).

License
-------

[](#license)

The MIT License. See [LICENSE.md](LICENSE.md).

###  Health Score

42

—

FairBetter than 89% of packages

Maintenance91

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 77.8% 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

98d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/2600024?v=4)[Thomson Maguru](/maintainers/tomsgad)[@tomsgad](https://github.com/tomsgad)

---

Top Contributors

[![tomsgad](https://avatars.githubusercontent.com/u/2600024?v=4)](https://github.com/tomsgad "tomsgad (7 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (2 commits)")

---

Tags

laravelTechLegendlaravel-notify-africa

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/mg-techlegend-laravel-notify-africa/health.svg)

```
[![Health](https://phpackages.com/badges/mg-techlegend-laravel-notify-africa/health.svg)](https://phpackages.com/packages/mg-techlegend-laravel-notify-africa)
```

###  Alternatives

[spatie/laravel-health

Monitor the health of a Laravel application

87411.3M153](/packages/spatie-laravel-health)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.1M337](/packages/psalm-plugin-laravel)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

815320.5k3](/packages/defstudio-telegraph)[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.3M42](/packages/spatie-laravel-pdf)[vormkracht10/laravel-mails

Laravel Mails can collect everything you might want to track about the mails that has been sent by your Laravel app.

24855.3k](/packages/vormkracht10-laravel-mails)[ralphjsmit/laravel-glide

Auto-magically generate responsive images from static image files.

4923.6k5](/packages/ralphjsmit-laravel-glide)

PHPackages © 2026

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