TypeScript SDK
npm install sendafrica — fully typed client with zero runtime dependencies, dual CJS+ESM output, phone utilities, and an SMS part calculator.
| Package | sendafrica on npm · v1.0.0 · stable |
| Install | npm install sendafrica |
| Requires | Node.js 18+ (global fetch) |
| Output | Dual CJS + ESM + .d.ts |
The TypeScript SDK wraps the REST API with full type safety — typed response objects, automatic retry with exponential backoff on 429/5xx errors, local phone normalization to E.164 before any network call, and an SMS part/credit calculator. Zero runtime dependencies.
#Installation
install.sh
bash
npm install sendafrica
# or
yarn add sendafrica
pnpm add sendafrica#Client setup
auth.ts
typescript
import { SendAfricaClient } from "sendafrica";
const client = new SendAfricaClient({
apiKey: process.env.SENDAFRICA_API_KEY!,
baseUrl: "https://api.sendafrica.online", // default
timeoutMs: 15_000, // per-request timeout
maxRetries: 3, // retries on 429/5xx (default)
fetch: myCustomFetch, // inject custom fetch (Node <18)
});#Sending SMS
send.ts
typescript
const result = await client.sendSms(
{
to: "0712345678",
message: "Your OTP is 123456",
from: "MyBrand", // optional, pre-approved sender ID
},
{ idempotencyKey: "order-1234" }, // safe retry key
);
console.log(result.messageId); // "SA-abc123..."
console.log(result.status); // "sent"
console.log(result.creditsUsed); // 1#Phone utilities & SMS calculator
Standalone pure functions — no client needed, zero network calls:
utils.ts
typescript
import {
normalizeTzPhone,
isValidTzPhone,
getSmsPartInfo,
detectEncoding,
} from "sendafrica";
normalizeTzPhone("0712345678"); // "+255712345678"
normalizeTzPhone("+255 712 345 678"); // "+255712345678"
isValidTzPhone("+255712345678"); // true
isValidTzPhone("+254712345678"); // false (not Tanzania)
getSmsPartInfo("Hello, your order is ready.");
// { encoding: "GSM-7", length: 28, parts: 1, creditsRequired: 1 }
detectEncoding("Hello 😊"); // "UCS-2"#Credits & payments
credits.ts
typescript
const balance = await client.getBalance();
console.log(balance.accountId, balance.balance);
const history = await client.getCreditHistory({ page: 1, perPage: 25 });
// Voucher top-up via mobile money
const rate = await client.getVoucherRate();
for (const tier of rate.tiers) {
console.log(tier.maxAmountTzs, tier.rateTzsPerCredit);
}
const voucher = await client.createVoucher({ provider: "snippe", amount: 50000 });
console.log(voucher.id, voucher.status, voucher.creditAmount);#Error handling
All errors inherit from SendAfricaError with typed properties .code, .httpStatus, .requestId, plus convenience getters .isInsufficientCredits, .isRateLimited, .isUnauthorized:
errors.ts
typescript
import { SendAfricaError, InvalidPhoneNumberError } from "sendafrica";
try {
await client.sendSms({ to: "0712345678", message: "Hello" });
} catch (err) {
if (err instanceof InvalidPhoneNumberError) {
// show "Please check the phone number"
} else if (err instanceof SendAfricaError) {
if (err.isInsufficientCredits) {
// top up credits
} else if (err.isRateLimited) {
// back off (SDK already retries automatically)
} else if (err.isUnauthorized) {
// check API key
} else {
console.error(err.code, err.message, err.requestId);
}
}
}