Node.js SDK
Official TypeScript client with retries, idempotency and webhook verification
The official Node.js SDK wraps the same REST API documented here. Everything is also reachable with plain HTTP — the SDK just adds retries, idempotency keys, auto-pagination and webhook signature verification.
Install
npm install southbill
Package: npmjs.com/package/southbill
Requires Node.js 18 or newer.
Create a checkout session
import { Southbill } from "southbill";
const southbill = new Southbill(process.env.SOUTHBILL_API_KEY);
const session = await southbill.checkout.sessions.create({
amount: 4900,
currency: "EUR",
customer_email: "ada@acme.com",
success_url: "https://acme.com/thanks",
});
console.log(session.checkout_url);
Available resources
| Namespace | Methods |
|---|---|
checkout.sessions |
create, retrieve, list, expire |
customers |
create, retrieve, update, list, del |
invoices |
create, retrieve, update, list, send, void, markPaid, listInstallments, listPayments |
products |
create, retrieve, update, list |
payments |
retrieve, list |
prices |
via products: listPrices, createPrice, setDefaultPrice |
refunds |
create, retrieve, list |
subscriptions |
create, retrieve, update, list, cancel |
subscriptionLinks |
create, retrieve, update, list, archive |
events |
retrieve, list, replay |
webhookEndpoints |
create, retrieve, update, del, list, rotateSecret, listDeliveries |
balance |
retrieve, transactions.retrieve, transactions.list |
Payouts, bank details, KYC and API-key management are deliberately absent — those actions stay merchant-controlled in the dashboard and are not exposed to any key or app.
Idempotency
Every POST sends an Idempotency-Key automatically. Supply your own so retries
across processes collapse into one operation:
await southbill.invoices.create(params, { idempotencyKey: `inv-${orderId}` });
Pagination
for await (const invoice of southbill.invoices.autoPagingEach({ status: "open" })) {
console.log(invoice.id);
}
Errors
Network failures, 429 and 5xx are retried twice with exponential backoff.
Everything else throws a SouthbillError carrying status, type, param and
requestId.
import { SouthbillError } from "southbill";
try {
await southbill.refunds.create({ payment: "pi_123", amount: 500 });
} catch (error) {
if (error instanceof SouthbillError) {
console.error(error.status, error.type, error.param, error.requestId);
}
}
Verify webhooks
Always verify the raw request body — not a re-serialized object.
import express from "express";
import { Southbill, SouthbillSignatureError } from "southbill";
const southbill = new Southbill(process.env.SOUTHBILL_API_KEY);
const app = express();
app.post("/webhooks/southbill", express.raw({ type: "*/*" }), (req, res) => {
try {
const event = southbill.webhooks.constructEvent({
payload: req.body,
signature: req.header("Southbill-Signature") ?? "",
secret: process.env.SOUTHBILL_WEBHOOK_SECRET,
});
if (event.type === "invoice.paid") {
// fulfil the order
}
res.sendStatus(200);
} catch (error) {
if (error instanceof SouthbillSignatureError) return res.sendStatus(400);
throw error;
}
});
Configuration
new Southbill({
apiKey: process.env.SOUTHBILL_API_KEY,
baseUrl: "https://api.southbill.com",
timeout: 30_000,
maxRetries: 2,
});
All merchant API keys are live keys (sk_live_...). Southbill has no test mode; southbill.livemode reports which mode the client runs in.