PHP SDK for the S-Mailer API — send messages, manage webhooks, and more

Maintainers

Package info

github.com/s-mailer/sdk-php

Homepage

pkg:composer/s-mailer/sdk

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-07-28 09:05 UTC

This package is auto-updated.

Last update: 2026-07-28 11:01:37 UTC


README

PHP SDK for the S-Mailer API — send messages across SMS, email, WhatsApp and more, list sent/received messages, check your balance, and verify webhooks.

Status: pre-1.0. All resource methods are implemented; the API may still change before 1.0.0.

Requirements

Installation

composer require s-mailer/sdk

If you don't already have a PSR-18 client installed:

composer require guzzlehttp/guzzle

Authentication

Every request authenticates with your X-Client-ID / X-Client-Secret pair. Find them in your S-Mailer dashboard.

$mailer = new \SMailer\Client('CLT-XXXXXXXX', 'your-api-secret');

Usage

Send a message

$result = $mailer->messages->send([
    'sender'    => 'sms-co1-xxxxxxxx',
    'recipients' => [
        '+258841234567',
        ['phone' => '+258842000001', 'data' => ['name' => 'Alice']],
    ],
    'content_template' => 'Hi ${name}, your code is ${code}.',
    // 'channel' => 'sms', 'provider' => 'sms-co1',  // alternatives to `sender`
    // 'campaign_id' => 'promo-2026',
    // 'webhook_url' => 'https://your-app.com/webhooks/status',
]);
// => ['message_id' => ..., 'external_id' => ..., 'total_cost_tokens' => ...]

List and fetch messages

$sent = $mailer->messages->listOutbound(['status' => 'delivered', 'channel' => 'sms', 'limit' => 50]);
$received = $mailer->messages->listInbound(['limit' => 50]);
$one = $mailer->messages->get($messageId);   // outbound or inbound; see `direction`

Retrigger / delete

$mailer->messages->retrigger($inboundId); // unlock + re-deliver a locked (unpaid) inbound
$mailer->messages->delete($messageId);    // delete an outbound or paid inbound message

Balance, profile, channels

$tokens   = $mailer->balance->get();       // int
$profile  = $mailer->balance->profile();   // includes webhook_signing_key
$channels = $mailer->channels->list();     // public senders + your private ones (authenticated)

Configuration

Pass a third $options array to tune the client:

$mailer = new \SMailer\Client('CLT-XXXXXXXX', 'your-api-secret', [
    'baseUrl'    => 'https://api.mailer.smartek.co.mz', // override the API host
    'maxRetries' => 2,                                  // retry 429/5xx/network (default 2)

    // Inject your own PSR-18 client / PSR-17 factories instead of auto-discovery:
    // 'httpClient'     => $psr18Client,
    // 'requestFactory' => $psr17RequestFactory,
    // 'streamFactory'  => $psr17StreamFactory,
]);

Retries use an exponential backoff and honour a Retry-After header on 429.

Webhooks

S-Mailer signs every webhook it sends with X-Mailer-Signature: hex(HMAC-SHA256(webhook_signing_key, raw_body)). Your webhook_signing_key comes from GET /api/v1/client ($mailer->balance->profile()). Verify over the raw request body, before decoding it.

$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_MAILER_SIGNATURE'] ?? '';

$event = (new \SMailer\Webhook())->parse($raw, $sig, $signingKey); // throws on bad signature
if (($event['payment_required'] ?? false) === false) {
    // handle inbound message / status update
}

Two payload shapes are delivered, distinguished by their fields:

  • Inbound: { id, from, to, channel, body, received_at, sender_id, client_id, payment_required }
  • Status: { message_id, recipient, status: { old, current }, error_code, timestamp }

Errors

Non-2xx responses raise a \SMailer\Exception\SMailerException subclass carrying the API error code (->errorCode) and HTTP status (->statusCode):

Status Exception (\SMailer\Exception\…) error
401 AuthenticationException AUTHENTICATION_FAILED
403 ForbiddenException FORBIDDEN
400 BadRequestException / ValidationException BAD_REQUEST / VALIDATION_ERROR
402 InsufficientTokensException INSUFFICIENT_TOKENS
404 NotFoundException NOT_FOUND
429 RateLimitException (->retryAfter) RATE_LIMIT_EXCEEDED
5xx ServerException INTERNAL_SERVER_ERROR

Network/transport failures raise NetworkException.

use SMailer\Exception\InsufficientTokensException;
use SMailer\Exception\RateLimitException;

try {
    $mailer->messages->send([/* ... */]);
} catch (InsufficientTokensException $e) {
    // top up the account
} catch (RateLimitException $e) {
    sleep($e->retryAfter ?? 1);
}

License

MIT — see LICENSE.