SDK
hisab-sdk wraps the REST API with full types, auto-pagination, automatic retries and webhook verification.
npm install hisab-sdk|TypeScript|Node.js 18+Install from npm. The SDK ships ESM and CommonJS builds with zero production dependencies.
npm install hisab-sdk
# or
yarn add hisab-sdk
# or
pnpm add hisab-sdkRequires Node.js 18 or newer. Your API key comes from the dashboard: Settings, then API keys.
Create a client with your API key, then create and finalize your first invoice.
import Hisab from 'hisab-sdk';
const hisab = new Hisab({ apiKey: process.env.HISAB_API_KEY });
// 1. create a draft invoice
const draft = await hisab.invoices.create({
customer_id: 'cus_2b81fe',
issue_date: '2026-06-05',
items: [
{ description: 'Integration services', quantity: 8, unit_price: 1300, tax_rate: 20 },
],
});
// 2. finalize it - this assigns the official invoice number
const invoice = await hisab.invoices.finalize(draft.id);
console.log(invoice.invoice_number); // FAC-2026-0142The full invoice lifecycle: draft, finalize, send, pay, void, plus PDF and UBL 2.1 exports.
// list with filters (paginated)
const { data, meta } = await hisab.invoices.list({ status: 'paid', page: 1 });
// lifecycle
const invoice = await hisab.invoices.get('inv_8f3a91');
await hisab.invoices.update('inv_8f3a91', { due_date: '2026-08-05' }); // drafts only
await hisab.invoices.finalize('inv_8f3a91');
await hisab.invoices.markAsSent('inv_8f3a91');
await hisab.invoices.markAsPaid('inv_8f3a91', { payment_method: 'bank_transfer' });
await hisab.invoices.void('inv_8f3a91', { reason: 'Duplicate billing' });
// exports
const pdf = await hisab.invoices.exportPdf('inv_8f3a91', { locale: 'fr' }); // ArrayBuffer
const xml = await hisab.invoices.exportXml('inv_8f3a91'); // UBL 2.1 stringCreate and manage B2B / B2C customers, search by name or ICE, and archive without losing history.
// create a B2B customer (ICE required for B2B)
const customer = await hisab.customers.create({
name: 'MERIT Sarl',
type: 'b2b',
ice: '001234567000089',
});
// read and update
const { data } = await hisab.customers.list({ type: 'b2b' });
await hisab.customers.update('cus_2b81fe', { email: 'finance@example.ma' });
// search helpers (built on list)
const matches = await hisab.customers.search('MERIT');
const byIce = await hisab.customers.findByIce('001234567000089');
// archiving keeps the invoice history
await hisab.customers.archive('cus_2b81fe');Schedules that generate invoices on the frequency you define, with pause / resume and on-demand generation.
// a monthly schedule that finalizes its invoices automatically
const recurring = await hisab.recurringInvoices.create({
customer_id: 'cus_2b81fe',
frequency: 'monthly',
start_date: '2026-07-01',
auto_finalize: true,
items: [{ description: 'Monthly retainer', quantity: 1, unit_price: 10400 }],
});
// control the schedule
await hisab.recurringInvoices.pause(recurring.id);
await hisab.recurringInvoices.resume(recurring.id, { next_run_date: '2026-08-01' });
// generate the next invoice on demand
const invoice = await hisab.recurringInvoices.generate(recurring.id);
// inspect past runs
const history = await hisab.recurringInvoices.getHistory(recurring.id);Read your profile, subscription and API quotas; update legal identifiers and contact info.
// read your organization profile, subscription and API quotas
const org = await hisab.organization.get();
console.log(org.quotas.api_rate_limit_per_minute); // 120
// update legal identifiers and contact info
await hisab.organization.update({ phone: '+212 5 22 00 00 01' });Verify the HMAC signature of incoming webhooks and parse events with type safety.
import { verifyWebhookSignature, parseWebhookEvent } from 'hisab-sdk';
// app/api/hisab-webhook/route.ts (Next.js)
export async function POST(req: Request) {
const payload = await req.text();
const valid = verifyWebhookSignature({
payload,
signature: req.headers.get('x-webhook-signature')!,
timestamp: req.headers.get('x-webhook-timestamp')!,
secret: process.env.HISAB_WEBHOOK_SECRET!,
});
if (!valid) return new Response('Invalid signature', { status: 401 });
const event = parseWebhookEvent(payload);
if (event.event === 'invoice.paid') {
// mark the order as paid in your system
}
return new Response('ok');
}Every API error maps to a typed class carrying the HTTP status, the stable error code and field-level details.
import {
ValidationError,
AuthenticationError,
NotFoundError,
RateLimitError,
HisabError,
} from 'hisab-sdk';
try {
await hisab.invoices.finalize('inv_8f3a91');
} catch (err) {
if (err instanceof ValidationError) {
console.error(err.details); // field-level issues
} else if (err instanceof RateLimitError) {
console.error('Retry after', err.retryAfter, 'seconds');
} else if (err instanceof NotFoundError) {
// unknown invoice id
} else if (err instanceof HisabError) {
console.error(err.code, err.message);
}
}Use page and per_page manually, or let listAll() iterate through every page for you.
// manual pagination
const page1 = await hisab.invoices.list({ page: 1, per_page: 50 });
console.log(page1.meta.pagination.total);
// auto-pagination - iterates every page for you
for await (const invoice of hisab.invoices.listAll({ status: 'paid' })) {
console.log(invoice.invoice_number);
}
// or collect everything at once
const allPaid = await hisab.invoices.listAll({ status: 'paid' }).toArray();