TypeScript SDK
Official TypeScript/JavaScript SDK for lomi. payments API.
Official Node.js/TypeScript SDK for lomi.. payments API. Works with Node.js, Deno, Bun, and browser environments.
Installation
bash npm install @lomi./sdk bash pnpm add @lomi./sdk bash yarn add @lomi./sdk bash bun add @lomi./sdk Before you start
- Create a lomi. account and complete onboarding.
- Copy a test secret key from Settings → Access tokens (
lomi_sk_test_…). See Access tokens. - Add it to a
.envfile (see Environment variables). - Install the SDK (below), set
environment: 'test', then run the first API call example.
Environments and base URLs
Use test while building. The SDK maps environment to the API host automatically:
environment | API base URL | Secret key prefix |
|---|---|---|
'test' | https://sandbox.api.lomi.africa | lomi_sk_test_… |
'live' | https://api.lomi.africa | lomi_sk_live_… |
You can override the host with baseUrl if needed, but matching key prefix and environment is enough for most integrations.
Quick start
import { LomiSDK } from '@lomi./sdk';
const lomi = new LomiSDK({
apiKey: process.env.LOMI_SECRET_KEY!,
environment: 'test', // sandbox: use 'live' only in production
});First API call (sandbox)
Copy-paste runnable example: install the SDK, set LOMI_SECRET_KEY in .env, then fetch your sandbox balance.
import { LomiSDK, LomiAuthError, LomiNotFoundError } from '@lomi./sdk';
const lomi = new LomiSDK({
apiKey: process.env.LOMI_SECRET_KEY!,
environment: 'test',
});
async function main() {
try {
const balance = await lomi.accounts.getBalance();
console.log('Balance:', balance);
} catch (error) {
if (error instanceof LomiAuthError) {
console.error(`Auth failed [${error.statusCode}]: ${error.message}`);
if (error.requestId) console.error('request_id:', error.requestId);
process.exit(1);
}
throw error;
}
}
main();LOMI_SECRET_KEY=lomi_sk_test_xxxxxxxxxxxxxxxxxxxxxxnpm install @lomi./sdk
# load .env with your tooling (dotenv, Next.js, etc.)
npx tsx first-call.tsExpected: balance payload from GET https://sandbox.api.lomi.africa/accounts/balance. For raw HTTP without the SDK, see API integration.
Payment Examples
Create a Checkout Session
const session = await lomi.checkoutSessions.create({
amount: 10000,
currency_code: 'XOF',
title: 'Premium Subscription',
description: 'Monthly access to premium features',
customer_email: 'customer@example.com',
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/cancel',
metadata: { order_id: 'ORD-123' },
});
console.log('Redirect to:', session.checkout_url);Create a Payment Link
const link = await lomi.paymentLinks.create({
link_type: 'product',
title: 'Pro Plan',
currency_code: 'XOF',
product_id: 'prod_abc123...',
allow_coupon_code: true,
});
console.log('Share this link:', link.url);List Transactions with Filters
const transactions = await lomi.transactions.list({
status: 'completed',
provider: 'WAVE',
startDate: '2024-01-01T00:00:00Z',
pageSize: 50,
});
for (const tx of transactions) {
console.log(`${tx.id}: ${tx.gross_amount} ${tx.currency_code}`);
}Customer Management
Create a Customer
const customer = await lomi.customers.create({
name: 'Amadou Ba',
email: 'amadou@example.com',
phone_number: '+221771234567',
country: 'Senegal',
city: 'Dakar',
metadata: { source: 'website' },
});
console.log('Customer ID:', customer.id);Get Customer Transactions
const transactions = await lomi.customers.getTransactions('cus_abc123...');Products & Subscriptions
Create a Product
const product = await lomi.products.create({
name: 'Premium Plan',
description: 'Full access to all features',
product_type: 'recurring',
prices: [
{
amount: 15000,
currency_code: 'XOF',
billing_interval: 'month',
is_default: true,
},
],
trial_enabled: true,
trial_period_days: 7,
});Add a New Price
const price = await lomi.products.addPrice('prod_abc123...', {
amount: 150000,
currency_code: 'XOF',
billing_interval: 'year',
});Cancel a Subscription
const cancelled = await lomi.subscriptions.cancel('sub_abc123...', {
cancellation_reason: 'Customer request',
});Payouts & rails-specific calls
Payout (POST /payouts)
await lomi.payouts.create({
destination: 'beneficiary',
rail: 'wave',
amount: 10000,
currency_code: 'XOF',
recipient: { name: 'Aicha Diallo', phone: '+221771234567' },
});Direct mobile-money charge (POST /charge/wave)
await lomi.charges.createWaveCharge({
amount: 5000,
currency: 'XOF',
customer: {
name: 'Moussa Ndiaye',
phoneNumber: '+221771234567',
email: 'moussa@example.com',
},
});Embedded card charge (POST /charge/card)
const charge = await lomi.charges.createCardCharge({
amount: 2500,
currency_code: 'XOF',
customer_email: 'buyer@example.com',
customer_name: 'Buyer Name',
});
// Use charge.data.client_secret with Payment Elements on your frontend
console.log(charge.data?.client_secret);Switch charge, server-side card authorization (POST /charge/switch)
For PCI-compliant fintech integrations that authorize cards server-side (not hosted checkout):
const result = await lomi.charges.createSwitchCharge(
{
amount: 10000,
currency_code: 'XOF',
pan: '4221941234569109',
expiry: '06/30',
cvv: '123',
card_holder_name: 'Amadou Ba',
},
{ idempotencyKey: 'switch-attempt-001' },
);
if (result.status === 'redirect_3ds') {
console.log('Complete 3DS at:', result.three_ds_url);
}Switch requires a PCI-DSS-compliant integration. For most merchants, use Payment Elements or checkout sessions instead.
Account & Organization
Get Account Balance
const balances = await lomi.accounts.getBalance();
const xofBalance = await lomi.accounts.getBalance({ currency: 'XOF' });
console.log('Available:', xofBalance.available);Get Organization Metrics
const metrics = await lomi.organizations.getMetrics();
console.log('MRR:', metrics.mrr);
console.log('Total Customers:', metrics.total_customers);Error Handling
The SDK throws typed LomiError subclasses with the API message, HTTP status, error code, and request_id:
import {
LomiSDK,
LomiAuthError,
LomiNotFoundError,
LomiValidationError,
LomiRateLimitError,
} from '@lomi./sdk';
try {
const customer = await lomi.customers.get('invalid_id');
} catch (error) {
if (error instanceof LomiNotFoundError) {
console.error('Customer not found:', error.message);
} else if (error instanceof LomiValidationError) {
console.error('Invalid request:', error.details);
} else if (error instanceof LomiRateLimitError) {
console.error('Rate limited, retry later');
} else if (error instanceof LomiAuthError) {
console.error(`Auth error [${error.statusCode}]: ${error.message}`);
}
console.error('request_id:', error.requestId);
}Configuration Options
Each LomiSDK instance is isolated, safe for multi-tenant workers and tests:
const lomi = new LomiSDK({
apiKey: process.env.LOMI_SECRET_KEY!,
environment: 'test', // 'live' in production
account: 'acct_connected_…', // default Lomi-Account header (operator mode)
timeout: 30_000, // request timeout in ms (default: 30000)
retries: 2, // retry idempotent GETs + 429s with backoff
headers: { 'X-Custom': '1' },
});Per-request options
Pass on any create-style call:
await lomi.checkoutSessions.create(body, {
idempotencyKey: 'checkout-001', // Idempotency-Key header
account: 'acct_other_…', // override Lomi-Account for this call
signal: abortController.signal, // AbortSignal
});Webhooks
Verify incoming webhook signatures without hand-rolling HMAC:
const valid = lomi.webhooks.verifySignature(
rawBody, // string or Buffer (use raw body, not parsed JSON)
req.headers['x-lomi-signature'],
process.env.LOMI_WEBHOOK_SECRET!,
);
if (!valid) return res.status(401).send('Invalid signature');Auto-pagination
List endpoints expose an async iterator via listAll:
for await (const tx of lomi.transactions.listAll({ status: 'completed' })) {
console.log(tx.id, tx.gross_amount);
}Payment Elements
import { loadLomi } from '@lomi./sdk';
// Your lomi_pk_… is validated; Elements run on lomi.'s PCI platform infrastructure.
const elements = await loadLomi('lomi_pk_test_…');Payment Elements initialize on lomi.'s platform account. Your publishable key identifies your merchant context; you never handle raw card numbers.
Available Services
| Service | Methods |
|---|---|
accounts | getBalance, getBalanceBreakdown, checkBalance |
charges | createWaveCharge, createMtnCharge, createCardCharge, createSwitchCharge, getCardCharge, cancelCardCharge |
checkoutSessions | list, listAll, get, create |
customers | list, listAll, get, create, update, delete, getTransactions, createPortalLaunchSession, getPortalAudit |
discountCoupons | list, listAll, get, create, getPerformance |
disputes | list, listAll, get |
logs | list, listAll, get |
merchants | get, getArr, getBalance, getMrr |
meters | list, listAll, get, create, update, getCustomerBalance |
organization | get |
organizations | list, listAll, get, getMetrics |
paymentLinks | list, listAll, get, create |
paymentRequests | list, listAll, get, create |
payouts | create, list, listAll, get |
products | list, listAll, get, create, addPrice, setDefaultPrice |
providers | list, listAll |
refunds | create, list, listAll, get |
riskAssessments | list, listAll, get |
settlements | findAll, findAllAll, findTransactions |
subscriptions | list, listAll, get, findByCustomer, cancel, uncancel, changePlan, update |
transactions | list, listAll, get |
usageBilling | checkEntitlement, listPeriods, getRevenue, getSubscriptionUsage, grantCredits, createEntitlement |
usageEvents | list, listAll, get, create |
usageSubscriptions | create |
webhookDeliveryLogs | list, listAll, get |
webhooks | list, listAll, get, create, update, delete, test, retryDelivery, verifySignature |
TypeScript types
Request and response bodies are fully typed from the OpenAPI schema:
import type {
CreateSwitchCharge,
SwitchChargeResponse,
components,
paths,
} from '@lomi./sdk';
// DTO aliases (generated from components.schemas)
type SwitchBody = CreateSwitchCharge;
// Or reference paths directly
type CheckoutCreate =
paths['/checkout-sessions']['post']['requestBody']['content']['application/json'];Database row types also ship for advanced integrations:
import type { Database } from '@lomi./sdk';
type Customer = Database['public']['Tables']['customers']['Row'];