Node.js / TypeScript 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.
- Node.js 18+ (uses the global
fetch), or a browser withfetch.
npm install @s-mailer/sdkEvery request authenticates with your X-Client-ID / X-Client-Secret pair (from your dashboard).
import { SMailer } from '@s-mailer/sdk';
const mailer = new SMailer({
clientId: 'CLT-XXXXXXXX',
clientSecret: 'your-api-secret',
});const mailer = new SMailer({
clientId: 'CLT-XXXXXXXX',
clientSecret: 'your-api-secret',
baseUrl: 'https://api.mailer.smartek.co.mz', // override the API host
timeout: 30000, // per-request timeout in ms (default 30000)
maxRetries: 2, // retry 429/5xx/network (default 2)
});Retries use an exponential backoff and honour a Retry-After header on 429.
const res = await mailer.messages.send({
sender: 'sms-co1-xxxxxxxx',
recipients: [
'+258841234567',
{ phone: '+258842000001', data: { name: 'Alice' } },
],
contentTemplate: 'Hi ${name}, your code is ${code}.',
// channel: 'sms', provider: 'sms-co1', // alternatives to `sender`
// campaignId: 'promo-2026',
// webhookUrl: 'https://your-app.com/webhooks/status',
});
// res.message_id, res.total_cost_tokens, ...const sent = await mailer.messages.listOutbound({ status: 'delivered', channel: 'sms', limit: 50 });
const received = await mailer.messages.listInbound({ limit: 50 });
const one = await mailer.messages.get(messageId); // outbound or inbound; check `direction`await mailer.messages.retrigger(inboundId); // unlock + re-deliver a locked (unpaid) inbound
await mailer.messages.delete(messageId); // delete an outbound or paid inbound messageconst tokens = await mailer.balance.get(); // number
const profile = await mailer.balance.profile(); // includes webhook_signing_key
const channels = await mailer.channels.list(); // public senders + your private ones (authenticated)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 mailer.balance.profile(). Verify over the raw request body, before parsing it.
import { webhooks } from '@s-mailer/sdk';
// Express example (raw body required)
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const raw = req.body.toString('utf8');
const sig = req.header('X-Mailer-Signature') ?? '';
const event = webhooks.parse(raw, sig, signingKey); // throws on bad signature
if (event.type === 'inbound' && !event.payment_required) {
// handle inbound message
} else if (event.type === 'status') {
// handle delivery status change
}
res.sendStatus(200);
});WebhookEvent is a discriminated union of InboundEvent (type: 'inbound') and StatusEvent (type: 'status').
Non-2xx responses reject with a typed error extending SMailerError, carrying the API error code and HTTP statusCode:
| Status | Error class | error |
|---|---|---|
| 401 | AuthenticationError |
AUTHENTICATION_FAILED |
| 403 | ForbiddenError |
FORBIDDEN |
| 400 | BadRequestError / ValidationError |
BAD_REQUEST / VALIDATION_ERROR |
| 402 | InsufficientTokensError |
INSUFFICIENT_TOKENS |
| 404 | NotFoundError |
NOT_FOUND |
| 429 | RateLimitError (retryAfter) |
RATE_LIMIT_EXCEEDED |
| 5xx | ServerError |
INTERNAL_SERVER_ERROR |
MIT — see LICENSE.