JavaScript / Node.js
Node 18+ has fetch built
in — no dependency needed. Examples in fetch and
axios (still common in older codebases).
const API_KEY = process.env.LISOLOO_API_KEY;const API_URL = "$BASE_URL/api/v1/lisoloo/sms-api";const HEADERS = { "app-key": API_KEY, "Content-Type": "application/json",};Send an SMS
Section titled “Send an SMS”const r = await fetch(`${API_URL}/send`, { method: "POST", headers: HEADERS, body: JSON.stringify({ to: ["+243998857000"], message: "Hello from Lisoloo!", sender_id: "MYAPP", }),});
if (!r.ok) { const err = await r.json(); throw new Error(`SMS failed: ${err.error_code} ${err.message}`);}
const { data } = await r.json();console.log(data.message_id);import axios from "axios";
const client = axios.create({ baseURL: API_URL, headers: HEADERS, timeout: 10_000,});
const { data } = await client.post("/send", { to: ["+243998857000"], message: "Hello from Lisoloo!", sender_id: "MYAPP",});
console.log(data.data.message_id);A reusable client class
Section titled “A reusable client class”type SendingType = "immediate" | "scheduled" | "recurring";
interface ScheduledDate { date: string; time: string }interface RecurringSchedule { start_date: string; end_date: string; frequency: "daily" | "weekly" | "monthly"; interval: number; times: string[];}
interface SendOptions { to: string[]; message: string; sender_id?: string; sending_type?: SendingType; scheduled_dates?: ScheduledDate[]; recurring_schedule?: RecurringSchedule; callback_url?: string;}
export class LisolooClient { constructor( private readonly apiKey: string, private readonly baseUrl = "$BASE_URL/api/v1/lisoloo/sms-api", private readonly timeoutMs = 10_000, ) {}
private headers() { return { "app-key": this.apiKey, "Content-Type": "application/json", }; }
private async request<T>( path: string, init: RequestInit = {}, ): Promise<T> { const ctrl = AbortSignal.timeout(this.timeoutMs); const r = await fetch(`${this.baseUrl}${path}`, { ...init, headers: this.headers(), signal: ctrl, }); if (!r.ok) { const body = await r.json().catch(() => ({} as Record<string, unknown>)); throw Object.assign(new Error(`Lisoloo ${r.status}`), { status: r.status, body }); } return r.json() as Promise<T>; }
async send(opts: SendOptions) { const body = { sending_type: "immediate", ...opts }; const r = await this.request<{ data: { message_id: string; status: string; total_cost: number } }>( "/send", { method: "POST", body: JSON.stringify(body) }, ); return r.data; }
async getStatus(messageId: string) { const r = await this.request<{ data: unknown }>(`/status/${messageId}`); return r.data; }
async getBalance() { const r = await this.request<{ data: { balance: number; available_sms: number } }>(`/balance`); return r.data; }}Usage:
const client = new LisolooClient(process.env.LISOLOO_API_KEY!);
// Instantconst sent = await client.send({ to: ["+243998857000"], message: "Hello!", sender_id: "MYAPP",});
// Scheduledawait client.send({ to: ["+243998857000"], message: "Reminder", sending_type: "scheduled", scheduled_dates: [{ date: "2026-06-01", time: "08:00" }],});
// Recurringawait client.send({ to: ["+243998857000"], message: "Weekly", sending_type: "recurring", recurring_schedule: { start_date: "2026-06-01", end_date: "2026-12-31", frequency: "weekly", interval: 1, times: ["09:00"], },});Handling 429 with retry
Section titled “Handling 429 with retry”async function sendWithRetry(client: LisolooClient, opts: SendOptions, max = 3) { for (let attempt = 0; attempt < max; attempt++) { try { return await client.send(opts); } catch (err: any) { if (err.status !== 429 || attempt === max - 1) throw err; const wait = (err.body?.retry_after ?? 60) * 1000; await new Promise(res => setTimeout(res, wait)); } } throw new Error("unreachable");}Express webhook receiver
Section titled “Express webhook receiver”import express from "express";
const app = express();app.use(express.json());
const EXPECTED_USER = process.env.LISOLOO_WEBHOOK_USER!;const EXPECTED_PASS = process.env.LISOLOO_WEBHOOK_PASS!;const seen = new Set<string>(); // in production: Redis
app.post("/lisoloo/webhook", (req, res) => { const auth = req.headers.authorization ?? ""; if (!auth.startsWith("Basic ")) return res.sendStatus(401); const [user, pass] = Buffer.from(auth.slice(6), "base64").toString().split(":"); if (user !== EXPECTED_USER || pass !== EXPECTED_PASS) { return res.sendStatus(401); }
const { event_id, event_type, message_id, data } = req.body; if (seen.has(event_id)) return res.sendStatus(204); seen.add(event_id);
// … your business logic … res.sendStatus(204);});
app.listen(3000);